Daniel Lip
Daniel Lip

Reputation: 11325

How can I find the center position of two or more objects?

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

public class MoveCameraBehind : MonoBehaviour
{
    public GameObject camera;
    public List<GameObject> targets = new List<GameObject>();
    public float cameraDistance = 10.0f;
    public bool behindMultipleTargets = false;
    public string cameraWarningMsgs = "";
    public string targetsWarningMsgs = "";

    // Use this for initialization
    void Start()
    {
        if (camera == null)
        {
            var cam = GetComponent<Camera>();
            if (cam != null)
            {
                cameraWarningMsgs = "Gettig camera component.";

                camera = transform.gameObject;
            }
            else
            {
                cameraWarningMsgs = "Creating a new camera component.";

                GameObject NewCam = Instantiate(new GameObject(), transform);
                NewCam.name = "New Camera";
                NewCam.AddComponent<Camera>();
                camera = NewCam;
            }
        }

        if(targets.Count == 0)
        {
            targetsWarningMsgs = "No targets found.";
        }
    }

    void FixedUpdate()
    {
        if (targets.Count > 0)
        {
            MoveCameraToPosition();
        }
    }

    public void MoveCameraToPosition()
    {
        if (targets.Count > 1 && behindMultipleTargets == true)
        {
            var center = CalculateCenter();
            transform.position = new Vector3(center.x, center.y + 2, center.z + cameraDistance);
        }

        if (behindMultipleTargets == false)
        {
            Vector3 center = targets[0].transform.position - targets[0].transform.forward * cameraDistance;
            transform.position = new Vector3(center.x, center.y + 2, center.z);
        }
    }

    private Vector3 CalculateCenter()
    {
        Vector3 center = new Vector3();

        var totalX = 0f;
        var totalY = 0f;
        foreach (var target in targets)
        {
            totalX += target.transform.position.x;
            totalY += target.transform.position.y;
        }
        var centerX = totalX / targets.Count;
        var centerY = totalY / targets.Count;

        center = new Vector3(centerX, centerY);

        return center;
    }
}

The CalculateCenter function make the targets(objects) to change positions and vanish away far away. Even if there is only one single target.

What I want to do is if there is one object for example one 3d cube position the camera behind the cube. And if there are more cubes for example two or ten and the camera is somewhere else calculate the middle position behind the targets and position the camera in the middle behind them.

To show what I mean in this example the view(like a camera) is behind the two soldiers in the middle position between them from behind.

But what if there are 5 soldiers how can I find the middle position and then position the camera behind them like this example in the screenshot ?

Example view

This is my old script version was working fine but only for 1 or 2 targets. But if there are 5 targets(soldiers) how can I position the camera behind them in the middle ? Like in the screenshot example.

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

public class MoveCameraBehind : MonoBehaviour
{
    public GameObject camera;
    public List<GameObject> targets = new List<GameObject>();
    public float cameraDistance = 10.0f;
    public bool behindTwoTargets = false;
    public string warningMsgs = "";

    // Use this for initialization
    void Start()
    {
        if (camera == null)
        {
            var cam = GetComponent<Camera>();
            if (cam != null)
            {
                warningMsgs = "Gettig Camera omponent.";

                camera = transform.gameObject;
            }
            else
            {
                warningMsgs = "Creating a new camera component.";

                GameObject NewCam = Instantiate(new GameObject(), transform);
                NewCam.name = "New Camera";
                NewCam.AddComponent<Camera>();
                camera = NewCam;
            }
        }

        camera.transform.Rotate(0, 180, 0);

    }

    void FixedUpdate()
    {
        if (targets.Count > 0)
        {
            MoveCameraToPosition();
        }
    }

    public void MoveCameraToPosition()
    {
        if (targets.Count == 2 && behindTwoTargets == true)
        {
            Vector3 center = ((targets[0].transform.position - targets[1].transform.position) / 2.0f) + targets[1].transform.position;
            camera.transform.position = new Vector3(center.x, center.y + 2, center.z + cameraDistance);
        }

        if (behindTwoTargets == false)
        {
            Vector3 center = targets[0].transform.position - targets[0].transform.forward * cameraDistance;
            camera.transform.position = new Vector3(center.x, center.y + 2, center.z);
        }
    }
}

This is my last version of my working code still using the CalculateCenter function :

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

public class CameraLook : MonoBehaviour
{
    public GameObject camera;
    public List<GameObject> targets = new List<GameObject>();
    public float cameraDistance = 10.0f;
    public float cameraHeight = 2f;
    public float rotateTime = 2f;
    public bool multipleTargets = false;
    public bool changeRandomTarget = false;
    public bool behindFront = false;
    public bool targetsRandomRot = false;
    public string cameraWarningMsgs = "";
    public string targetsWarningMsgs = "";

    private List<Vector3> vectors = new List<Vector3>();

    //Random move rotation timer part
    Quaternion qTo;
    float speed = 3f;
    float timer = 0.0f;

    // Use this for initialization
    void Start()
    {
        qTo = Quaternion.Euler(new Vector3(0.0f, Random.Range(-180.0f, 180.0f), 0.0f));

        if (camera == null)
        {
            var cam = GetComponent<Camera>();
            if (cam != null)
            {
                cameraWarningMsgs = "Gettig camera component.";

                camera = transform.gameObject;
            }
            else
            {
                cameraWarningMsgs = "Creating a new camera component.";

                GameObject NewCam = Instantiate(new GameObject(), transform);
                NewCam.name = "New Camera";
                NewCam.AddComponent<Camera>();
                camera = NewCam;
            }
        }

        if (targets.Count == 0)
        {
            targetsWarningMsgs = "No targets found.";
        }
        else
        {
            foreach(GameObject vector in targets)
            {
                vectors.Add(vector.transform.position);
            }
        }
    }

    void FixedUpdate()
    {
        if (targets.Count > 0)
        {
            MoveCameraToPosition();

            if (targetsRandomRot == true)
            {
                RotateTargetsRandom();
            }
        }
    }

    public void MoveCameraToPosition()
    {
        Vector3 center = CalculateCenter();
        camera.transform.position = center;

        if (behindFront == false)
        {
            camera.transform.rotation = Quaternion.LookRotation(-center, Vector3.up);
        }
        else
        {
            camera.transform.rotation = Quaternion.LookRotation(center, Vector3.up);
        }
    }

    private Vector3 CalculateCenter()
    {
        Vector3 center = new Vector3();

        var x = targets[0].transform.position.x;
        var y = targets[0].transform.position.y;
        var z = targets[0].transform.position.z;

        if (multipleTargets == true)
        {
            for (int i = 1; i < targets.Count; i++)
            {
                x += targets[i].transform.position.x;
                y += targets[i].transform.position.y;
                z += targets[i].transform.position.z;
            }
        }
        else
        {
            x += targets[0].transform.position.x;
            y += targets[0].transform.position.y;
            z += targets[0].transform.position.z;
        }

        if(changeRandomTarget == true)
        {
            for (int i = 1; i < targets.Count; i++)
            {
                x += targets[i].transform.position.x;
                y += targets[i].transform.position.y;
                z += targets[i].transform.position.z;
            }
        }

        x = x / targets.Count;
        y = y / targets.Count;
        z = z / targets.Count;

        if (behindFront == false)
        {
            center = new Vector3(x, y + cameraHeight, z + cameraDistance);
        }
        else
        {
            center = new Vector3(x, y + cameraHeight, z - cameraDistance);
        }

        return center;
    }

    private void RotateTargetsRandom()
    {
        timer += Time.deltaTime;

        if (timer > rotateTime)
        { // timer resets at 2, allowing .5 s to do the rotating
            qTo = Quaternion.Euler(new Vector3(0.0f, Random.Range(-180.0f, 180.0f), 0.0f));
            timer = 0.0f;
        }

        foreach (var target in targets)
        {
            target.transform.rotation = Quaternion.Slerp(target.transform.rotation, qTo, Time.deltaTime * speed);
        }
    }
}

And now I want to add and use the bool flag changeRandomTarget : But not sure how to do it :

if(changeRandomTarget == true)
        {
            for (int i = 1; i < targets.Count; i++)
            {
                x += targets[i].transform.position.x;
                y += targets[i].transform.position.y;
                z += targets[i].transform.position.z;
            }
        }

I want that each X seconds it will pick a random center and change the camera position according to it. For example the first item in the targets List the last item and all the items so each X seconds the center will be once behind targets[0] or targets1 or targets[i]

Not sure how to do it with my code or with derHugo solution.

Upvotes: 1

Views: 1047

Answers (2)

derHugo
derHugo

Reputation: 90714

Actually you don't even need to do it component wise:

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

...

private static Vector3 Average(IReadOnlyCollection<Vector3> vectors)
{
    if(vectors == null) return Vector3.zero;

    switch (vectors.Count)
    {
        case 0:
            return Vector3.zero;

        case 1:
            return vectors.First();

        default:
            var average = Vector3.zero;
            foreach(var vector in vectors)
            {
                average += vector;
            }
            return average / vectors.Count;
    }
}

Or directly using Linq aggregate

private static Vector3 Average(IReadOnlyCollection<Vector3> vectors)
{
    if(vectors == null) return Vector3.zero;

    switch (vectors.Count)
    {
        case 0:
            return Vector3.zero;

        case 1:
            return vectors.First();

        default:
            var average = vectors.Aggregate(Vector3.zero, (current, vector) => current + vector);
            return average / vectors.Count;
    }
}

Upvotes: 1

BugFinder
BugFinder

Reputation: 17868

Surely you just find the average

so

if (mobcount > 1)
{
    var x=mob[0].position.x;
    var y=mob[0].position.y;
    var z=mob[0].position.z;

   for(int i=1; i<mobcount; i++)
   {
      x += mob[i].position.x;
      y += mob[i].position.y;
      z += mob[i].position.z;
   }

   x = x / mobcount;
   y = y / mobcount;
   z = z / mobcount;
}

therefore the camera should look at the position x,y,z.. and perhaps set the distance to be a fixed distance behind the nearest mob...

Upvotes: 3

Related Questions