0
\$\begingroup\$

Some additional remarks before I address the issue:

How Viewports work in Unity:

Whatever the dimensions of the screen's rect, the viewports coordinates are mapped in normalized coordinates, where 0 represents left and bottom, for x and y respectively, and 1 right and top, for x and y respectively (image below).

enter image description here

Follow a Target (Easy implementation for the sake of demonstration):

Consider this following simple algorithm to follow a target:

CameraFollowExample : MonoBehaviour
{
    public Transform target;
    public bool relativeToTarget;
    public Vector3 followOffset;

    private void LateUpdate()
    {
        if(target != null) FollowTarget();
    }

    private void FollowTarget()
    {
        transform.position = target.position + (relativeToTarget ? target.TransformDirection(followOffset) : followOffset);
        transform.Lookat(target);
    }
}

Issue:

The following works as expected, but the target will always be at the viewport's center ({ 0.0, 0.0 })

What I am trying to Achieve:

This is what I am trying to achieve: make camera follow a given target, with an additional offset from the viewport's center. This offset being additional to the following offset applied to the target.

So for example, if I define two ranged floats, centerX and centerY respectively, I'd like the camera to be positioned with an additional offset, so the defined offset is at the target. As an example, on the image below, I have centerX and centerY on { 0.2, 0.25 } respectively, so the target should be at that viewport's point.

enter image description here

Having a resulting (partial) script:

CameraFollowExample : MonoBehaviour
{
    public Transform target;
    public bool relativeToTarget;
    public Vector3 followOffset;
    [Range(0.0f, 1.0f)] public float centerX;
    [Range(0.0f, 1.0f)] public float centerY;

    private void LateUpdate()
    {
        if(target != null) FollowTarget();
    }

    private void FollowTarget()
    {
        Vector3 destination = target.position + (relativeToTarget ? target.TransformDirection(followOffset) : followOffset);
        transform.position = destination + GetCalculatedOffset();
        transform.Lookat(target);
    }

    private Vector3 GetCalculatedOffset()
    {
        // interpret the Vector { centerX, centerY } to return an offset.
        // return (interpretedOffset - target.position);
    }
}

What I have already tried:

  • Make the camera position to the destination plus an offset of the center [{centerX, centerY} - {0.5, 0.5}], the direction is ok, but obviously, the target is not positioned at the expected viewports' point coordinate.
  • Have a global vector that is the custom center point relative to the camera's transform and the nearPlane, and then make an offset between that point and the target, ignoring the z. The camera gets away from the target.

What I am not trying to achieve:

Something like following example (define an additional offset vector, and position the camera with the additional offset):

CameraFollowExample : MonoBehaviour
{
    public Transform target;
    public bool relativeToTarget;
    public Vector3 followOffset;
    public Vector3 additionalOffset;

    private void LateUpdate()
    {
        if(target != null) FollowTarget();
    }

    private void FollowTarget()
    {
        Vector3 destination = target.position + (relativeToTarget ? target.TransformDirection(followOffset) : followOffset);
        transform.position = destination + additionalOffset;
        transform.Lookat(target);
    }
}

I mean, this technically works, since the target won't be at the center of the viewport. But the intention is for the target to be positioned at the defined viewport's point.

Any additional information you may need, let me know. Thank you beforehand, and Happy New Year.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

If you want to know which way a particular viewport point faces in the world, you can just ask the camera using the ViewportPointToRay method.

[RequireComponent(typeof(Camera))]
public class OffCenterFollow : MonoBehaviour {

    public Transform target;
    public float targetDistance;
    public Vector2 viewportPosition;

    new Camera camera;

    private void OnEnable() {
        camera = GetComponent<Camera>();
    }

    void LateUpdate () {
        var ray = camera.ViewportPointToRay(viewportPosition);

        Vector3 fromTarget = -ray.direction * targetDistance;
        if(camera.orthographic) {
            Vector3 shift = transform.InverseTransformPoint(ray.origin);
            shift.z = 0f;
            fromTarget -= transform.TransformVector(shift);
        }

        transform.position = target.position + fromTarget;
    }
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .