c# - 如何防止在地形下移动相机 View ?

标签 c# unity3d unity5

我正在使用这个连接到相机的 mouseorbit 脚本。
问题是当我用鼠标移动相机并旋转它以使相机位于地形下方时。
我希望当它到达地形高度然后停止不要向下移动我的意思是不要在角色最大值下到达这个 View 以处于地形高度..

Mouse Orbit

要停在地形高度上,我的意思是这样:

Mouse Orbit 001

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

public class MouseOrbit : MonoBehaviour
{
    /* These variables are what tell the camera how its going to function by
      * setting the viewing target, collision layers, and other properties
      * such as distance and viewing angles */
    public Transform viewTarget;
    public LayerMask collisionLayers;
    public float distance = 6.0f;
    public float distanceSpeed = 150.0f;
    public float collisionOffset = 0.3f;
    public float minDistance = 4.0f;
    public float maxDistance = 12.0f;
    public float height = 1.5f;
    public float horizontalRotationSpeed = 250.0f;
    public float verticalRotationSpeed = 150.0f;
    public float rotationDampening = 0.75f;
    public float minVerticalAngle = -60.0f;
    public float maxVerticalAngle = 60.0f;
    public bool useRMBToAim = false;

    /* These variables are meant to store values given by the script and
     * not the user */
    private float h, v, smoothDistance;
    private Vector3 newPosition;
    private Quaternion newRotation, smoothRotation;
    private Transform cameraTransform;

    /* This is where we initialize our script */
    void Start()
    {
        Initialize();
    }

    /* This is where we set our private variables, check for null errors,
     * and anything else that needs to be called once during startup */
    void Initialize()
    {
        h = this.transform.eulerAngles.x;
        v = this.transform.eulerAngles.y;

        cameraTransform = this.transform;
        smoothDistance = distance;

        NullErrorCheck();
    }

    /* We check for null errors or warnings and notify the user to fix them */
    void NullErrorCheck()
    {
        if (!viewTarget)
        {
            Debug.LogError("Please make sure to assign a view target!");
            Debug.Break();
        }
        if (collisionLayers == 0)
        {
            Debug.LogWarning("Make sure to set the collision layers to the layers the camera should collide with!");
        }
    }

    /* This is where we do all our camera updates. This is where the camera
     * gets all of its functionality. From setting the position and rotation,
     * to adjusting the camera to avoid geometry clipping */
    void LateUpdate()
    {
        if (!viewTarget)
            return;

        /* We check for right mouse button functionality, set the rotation
         * angles, and lock the mouse cursor */
        if (!useRMBToAim)
        {
            /* Check to make sure the game isn't paused and lock the mouse cursor*/
            if (Time.timeScale > 0.0f)
                Cursor.lockState = CursorLockMode.Locked;

            h += Input.GetAxis("Mouse X") * horizontalRotationSpeed * Time.deltaTime;
            v -= Input.GetAxis("Mouse Y") * verticalRotationSpeed * Time.deltaTime;

            h = ClampAngle(h, -360.0f, 360.0f);
            v = ClampAngle(v, minVerticalAngle, maxVerticalAngle);

            newRotation = Quaternion.Euler(v, h, 0.0f);
        }
        else
        {
            if (Input.GetMouseButton(1))
            {
                /* Check to make sure the game isn't paused and lock the mouse cursor */
                if (Time.timeScale > 0.0f)
                    Cursor.lockState = CursorLockMode.Locked;

                h += Input.GetAxis("Mouse X") * horizontalRotationSpeed * Time.deltaTime;
                v -= Input.GetAxis("Mouse Y") * verticalRotationSpeed * Time.deltaTime;

                h = ClampAngle(h, -360.0f, 360.0f);
                v = ClampAngle(v, minVerticalAngle, maxVerticalAngle);

                newRotation = Quaternion.Euler(v, h, 0.0f);
            }
            else
            {
                Cursor.lockState = CursorLockMode.Confined;
            }
        }

        /* We set the distance by moving the mouse wheel and use a custom
         * growth function as the time value for linear interpolation */
        distance = Mathf.Clamp(distance - Input.GetAxis("Mouse ScrollWheel") * 10, minDistance, maxDistance);
        smoothDistance = Mathf.Lerp(smoothDistance, distance, TimeSignature(distanceSpeed));

        /*We give the rotation some smoothing for a nicer effect */
        smoothRotation = Quaternion.Slerp(smoothRotation, newRotation, TimeSignature((1 / rotationDampening) * 100.0f));

        newPosition = viewTarget.position;
        newPosition += smoothRotation * new Vector3(0.0f, height, -smoothDistance);

        /* Calls the function to adjust the camera position to avoid clipping */
        CheckSphere();

        smoothRotation.eulerAngles = new Vector3(smoothRotation.eulerAngles.x, smoothRotation.eulerAngles.y, 0.0f);

        cameraTransform.position = newPosition;
        cameraTransform.rotation = smoothRotation;
    }

    /* This is where the camera checks for a collsion hit within a specified radius,
     * and then moves the camera above the location it hit with an offset value */
    void CheckSphere()
    {
        /* Add height to our spherecast origin */
        Vector3 tmpVect = viewTarget.position;
        tmpVect.y += height;

        RaycastHit hit;

        /* Get the direction from the camera position to the origin */
        Vector3 dir = (newPosition - tmpVect).normalized;

        /* Check a radius for collision hits and then set the new position for
         * the camera */
        if (Physics.SphereCast(tmpVect, 0.3f, dir, out hit, distance, collisionLayers))
        {
            newPosition = hit.point + (hit.normal * collisionOffset);
        }
    }

    /* Keeps the angles values within their specificed minimum and maximum
     * inputs while at the same time putting the values back to 0 if they
     * go outside of the 360 degree range */
    private float ClampAngle(float angle, float min, float max)
    {
        if (angle < -360)
            angle += 360;

        if (angle > 360)
            angle -= 360;

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

    /* This is our custom logistic growth time signature with speed as input */
    private float TimeSignature(float speed)
    {
        return 1.0f / (1.0f + 80.0f * Mathf.Exp(-speed * 0.02f));
    }
}

看起来我抓取的脚本已经处理了地形碰撞......我只需要确保在其上设置碰撞层以包含地形。但不知道该怎么做。

我试过的:

我现在在检查器中添加了一个名为 Terrain 的新层。
然后在地形上的层次结构中,我将其图层更改为地形。
同样在脚本中我选择了地形。但它仍然无法正常工作。

在屏幕截图中,顶部是相机的检查器,在 Collision Layers Terrain 中选择了脚本。
在底部地形检查员选择地形作为:层

Tried

最佳答案

这个问题有很多答案。
但这是我的看法,因为我相当肯定这个解决方案与您将在网上找到的与统一第三人称 Controller 相关的所有解决方案相比很有趣。

摘要
在 Unity 中,您可以采用通常的方法,让一个空对象成为相机的父对象,等等……但业内人士通常会使用一个名为 Spherical coordinates 的数学公式。 .
到目前为止,根据我的经验,这是第 3 人称 Controller 的最佳方法,也是最优雅、最漂亮的方法。实现此方法后,您需要做的就是从相机射出一米的光线,如果检测到任何东西,则更改球坐标公式中的单个值,这将使玩家周围的球体变小,从而产生效果碰撞的
实现
这是一段代码,显示了创建球坐标公式所需的单个函数。

private Vector3 sphericalCoordinates()
{
    float m = distanceFromPlayer;
    v = mouseY;
    h = mouseX;

    x = m * Mathf.Sin(v) * Mathf.Cos(h);
    z = m * Mathf.Sin(v) * Mathf.Sin(h);
    y = m * Mathf.Cos(v);

    Vector3 pos = new Vector3(x, y, z);
    return pos;
}
m = 幅度。
您可以将其视为球体的半径,这是您更改以提供碰撞效果的原因。
我稍后使用此代码段将播放器与触发中的相机绑定(bind)。
[注意:非常不干净的代码,我只是想向你展示一个基本概念,以后一定要让它更干净]
Vector3 getCamRotWORLD()
{
    Vector3 camRotWORLD = new Vector3(0, transform.rotation.eulerAngles.y, 0);
    return camRotWORLD;
}

void adjustPlayerRot()
{
    if (Input.GetKey(KeyCode.W))
    {
        player.transform.rotation = Quaternion.Euler(player.transform.rotation.x, getCamRotWORLD().y, player.transform.rotation.z);
        player.transform.Translate(0, 0, playerScript.speed * Time.deltaTime);
    }

    if (Input.GetKey(KeyCode.A))
    {
        player.transform.rotation = Quaternion.Euler(player.transform.rotation.x, getCamRotWORLD().y - 90, player.transform.rotation.z);
        player.transform.Translate(0, 0, playerScript.speed * Time.deltaTime);
    }

    if (Input.GetKey(KeyCode.S))
    {
        player.transform.rotation = Quaternion.Euler(player.transform.rotation.x, getCamRotWORLD().y - 180, player.transform.rotation.z);
        player.transform.Translate(0, 0, playerScript.speed * Time.deltaTime);
    }

    if (Input.GetKey(KeyCode.D))
    {
        player.transform.rotation = Quaternion.Euler(player.transform.rotation.x, getCamRotWORLD().y + 90, player.transform.rotation.z);
        player.transform.Translate(0, 0, playerScript.speed * Time.deltaTime);
    }
}
这是我的更新的样子,以防您真的想抢夺实现而不是尝试自己尝试。
private void Update()
{
    mouseX -= Input.GetAxis("Mouse X") * (sensitivity * Time.deltaTime);
    mouseY -= Input.GetAxis("Mouse Y") * (sensitivity * Time.deltaTime);

    playerCam.transform.LookAt(player.transform);
    playerCam.transform.position = new Vector3(
        sphericalCoordinates().x + player.transform.position.x,
        sphericalCoordinates().y,
        sphericalCoordinates().z + player.transform.position.z
        );
}

关于c# - 如何防止在地形下移动相机 View ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45969734/

相关文章:

c# - Unity3d:音频处理或计算应该调用哪个类?

c# - 在 Unity C# 中实例化游戏对象列表

c# - 如何更好地将配置元素映射到应用程序对象?

c# - 在wpf中的两个按钮之间绘制线

Unity3D WebGL Headless 不渲染

c# - 是否可以录制游戏中的声音,将其保存,然后使用C#将其作为常规歌曲在手机上播放?

audio - Unity3d 脚本在使用 AddComponent() 时丢失 AudioClip 引用

unity3d - 旋转实例化预制件

c# - 生成后事件执行 PowerShell

c# - 如何动态获取本地 IP 广播地址 C#