unity3d 缩小摄像机以便它可以看到所有对象

vh0rcniy  于 2022-11-15  发布在  其他
关注(0)|答案(2)|浏览(162)

我有一个对象列表,这些是视频中的蓝色火柴人,我需要让摄像机自动移开,所有对象(蓝色火柴人)始终适合它,你需要考虑到每次都会有越来越多的对象,所以摄像机应该是动态的,并适应所有对象
https://youtube.com/shorts/x3uSO2L22Kc?feature=share

ogq8wdun

ogq8wdun1#

适用于任何摄像机Angular 和物体位置的实用解决方案

这里的想法相当简单
在每一步中,我们都会检查每个对象是否在摄像机视野内,或者摄像机是否太远,然后我们只需将摄像机位置调整到更好的位置。
我们的摄像机将逐步动态跟踪目标对象,当稳定后,摄像机将捕获所有目标对象。

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

public class CameraAutoFitSmooth : MonoBehaviour
{
    public int maxNumberOfObjs = 100;
    public GameObject objPrefab;
    public float maxInitRange = 10f;

    public float minCameraHeight = 1f;
    public float maxCameraMoveSpeed = 9f;
    public float marginalPos = 0.1f;

    [HideInInspector]
    public List<Transform> objs = new List<Transform>();
    Camera cam;

    void Start()
    {
        cam = Camera.main;
    }

    void Update() 
    {
        if (Input.GetKeyDown(KeyCode.Space))
            RandomObjs();
    }

    // Randomly regenerate objects
    void RandomObjs()
    {
        int nowNumberOfObjs = Random.Range(0, maxNumberOfObjs);
        for (int i = objs.Count - 1; i > nowNumberOfObjs; i--)
        {
            Destroy(objs[i].gameObject);
            objs.RemoveAt(i);
        }

        for (int i = objs.Count; i <= nowNumberOfObjs; i++)
            objs.Add(Instantiate(objPrefab).transform);
            
        foreach (var obj in objs)
            obj.position = Random.insideUnitSphere * maxInitRange;
    }

    void LateUpdate()
    {
        SetCameraFitPosition(Time.deltaTime);
    }

    void SetCameraFitPosition(float deltaTime)
    {
        Vector3 targetCamPos = cam.transform.position;
        if (objs.Count == 1)
        {
            targetCamPos = objs[0].position - minCameraHeight * cam.transform.forward;
        }
        else if (objs.Count > 1)
        {
            float minInsideDiff = 1f, maxOutsideDiff = 0f;
            Vector3 center = Vector3.zero;
            foreach (var obj in objs)
            {
                Vector3 screenPos = GetScreenPos(obj.position);

                if (IsInsideView(screenPos))
                    minInsideDiff = Mathf.Min(minInsideDiff, CalculateInsideDiff(screenPos.x), CalculateInsideDiff(screenPos.y), CalculateInsideDiff(screenPos.z));
                else
                    maxOutsideDiff = Mathf.Max(maxOutsideDiff, CalculateOutsideDiff(screenPos.x), CalculateOutsideDiff(screenPos.y), CalculateOutsideDiff(screenPos.z));

                center += obj.position;
            }
            center /= objs.Count;

            float nowHeight = Vector3.Project(cam.transform.position - center, cam.transform.forward).magnitude;
            float maxDiff = maxOutsideDiff > 0f ? maxOutsideDiff : -minInsideDiff;
            float finalHeight = Mathf.Max(nowHeight + maxDiff * maxCameraMoveSpeed, minCameraHeight);

            targetCamPos = center - finalHeight * cam.transform.forward;
        }

        cam.transform.position = Vector3.MoveTowards(cam.transform.position, targetCamPos, maxCameraMoveSpeed * deltaTime);
    }

    Vector3 GetScreenPos(Vector3 pos)
    {
        return cam.WorldToViewportPoint(pos);
    }
    float CalculateOutsideDiff(float pos)
    {
        float diff = 0f;
        if (pos > 1f + marginalPos)
            diff = pos - 1f;
        else if (pos < -marginalPos)
            diff = -pos;
        return diff;
    }
    float CalculateInsideDiff(float pos)
    {
        float diff = 0f;
        if (pos < 1f - marginalPos && pos > marginalPos)
            diff = Mathf.Min(1f - pos, pos);
        return diff;
    }
    bool IsInsideView(Vector3 screenPoint)
    {
        return screenPoint.z > 0f && screenPoint.x > 0f && screenPoint.x < 1 && screenPoint.y > 0f && screenPoint.y < 1;
    }
}

如果您需要更多信息,请随时联系我:)干杯!

czq61nw1

czq61nw12#

以下脚本将在自上而下的视图中放置一个透视摄像机,以便所有跟踪的游戏对象(objs)都可见。
假设对象位于零xz平面上,并且是点,因此不考虑它们的实际尺寸。必须至少有一个被跟踪对象。对象的间距不能要求摄像机高度超过最大浮点值。

public GameObject[] objs;//objects that must be fitted
private Camera cam;
float recXmin, recXmax, recYmin, recYmax;
Vector3 center;

void Start()
{
  cam = Camera.main;
  cam.transform.rotation = Quaternion.Euler(90, 0, 0);
}

void LateUpdate()
{
  recXmin = objs[0].transform.position.x;
  recXmax = objs[0].transform.position.x;
  recYmin = objs[0].transform.position.z;
  recYmax = objs[0].transform.position.z;
  center = Vector3.zero;

  foreach (GameObject obj in objs)
  {
    if (obj.transform.position.x < recXmin)
    {
    recXmin = obj.transform.position.x;
    }
    if (obj.transform.position.x > recXmax)
    {
    recXmax = obj.transform.position.x;
    }
    if (obj.transform.position.z < recYmin)
    {
    recYmin = obj.transform.position.z;
    }
    if (obj.transform.position.z > recYmax)
    {
    recYmax = obj.transform.position.z;
    }
  }

  float horizontalHeight = (recYmax - recYmin) / 2 / Mathf.Tan(Mathf.Deg2Rad * cam.fieldOfView / 2);
  float verticalHeight = (recXmax - recXmin) / 2 / Mathf.Tan(Mathf.Deg2Rad * Camera.VerticalToHorizontalFieldOfView(cam.fieldOfView, cam.aspect) / 2);
  float finalHeight = horizontalHeight > verticalHeight ? horizontalHeight : verticalHeight;
  center = new Vector3(recXmin + (recXmax - recXmin) / 2, finalHeight, recYmin + (recYmax - recYmin) / 2);
  cam.transform.position = center;
}

void OnDrawGizmos()
{
  Gizmos.color = Color.red;
  Gizmos.DrawLine(new Vector3(recXmin, 0, recYmin), new Vector3(recXmin, 0, recYmax));
  Gizmos.DrawLine(new Vector3(recXmax, 0, recYmin), new Vector3(recXmax, 0, recYmax));

  Gizmos.color = Color.green;
  Gizmos.DrawLine(new Vector3(recXmin, 0, recYmin), new Vector3(recXmax, 0, recYmin));
  Gizmos.DrawLine(new Vector3(recXmin, 0, recYmax), new Vector3(recXmax, 0, recYmax));

  Gizmos.color = Color.blue;
  Gizmos.DrawSphere(center, 0.5f);
}
  • 脚本确定由所有被跟踪对象形成的“边界正方形
  • 假设边界正方形是金字塔的基础,摄像机位于金字塔的顶点
  • 通过考虑已知的金字塔底边长度和摄像机视野,使用三角学可以计算摄像机的高度
  • 对摄像机的水平和垂直视场进行两次计算
  • 然后选择这两个值中的较大值,
  • 最后,将摄像机定位在棱锥底部的中间并处于确定的高度,从而使其在棱锥顶部结束

相关问题