Home > other >  Zooming out the camera so that it can see all objects
Zooming out the camera so that it can see all objects

Time:11-06

I have a list of objects, these are blue stickmen on the video, I need to make the camera move away by itself and all objects (blue stickmen) always fit into it, you need to take into account that there will be more and more objects each time, so the camera should be dynamic and adapt itself to all objects

Showcase

The idea here is fairly simple.

At each step we check if each object is inside of camera view or is camera too far away, then we simply adjust the camera position towards a better one.

Step by step, our camera will follow target objects dynamically, and when stabilized, all target objects will be captured by camera.

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

If you need more info feel free to contact me :) Cheers!

  • Related