0
\$\begingroup\$

I want to slide in an object into view, coming from the right of the screen.

In order to achieve this, I need to position the object next to the camera view, so that it is still not visible, but close enough for a quick slide in.

For example, placing the object at X=1000000 (so that it is surely outside the camera view) for safety wouldn't work as it couldn't slide in quickly enough. For a reasonable slide, I would need to have it really close next to the camera view.

How could I calculate the object's position for that?

Thank you.

Edit:

Here is the script that I'm now using and which I'm still having problems with:

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

public class NewBehaviourScript : MonoBehaviour
{
    void Update()
    {
        float fDist = Mathf.Abs(Camera.main.transform.position.z - this.transform.position.z);
        Bounds b = BoundsFromTransform(this.transform);

        this.transform.position = GetPointLeftOfCamera(Camera.main, fDist, b.size.magnitude / 2);
    }

    public static Bounds BoundsFromTransform(Transform uTransform)
    {

        Bounds bounds = new Bounds(uTransform.position, Vector3.zero);

        foreach (Renderer renderer in uTransform.GetComponentsInChildren<Renderer>())
        {
            bounds.Encapsulate(renderer.bounds);
        }

        Vector3 nOff = bounds.center - uTransform.position;

        return new Bounds(nOff, bounds.size);
    }
    public static Vector3 GetPointLeftOfCamera(Camera camera, float distance, float goDiameter)
    {
        // 1.
        var ray = camera.ScreenPointToRay(new Vector3(0, camera.pixelHeight / 2f, 0));

        // 2.
        var borderPoint = ray.GetPoint(distance);

        // 3.
        var leftPlane = GeometryUtility.CalculateFrustumPlanes(camera)[0];
        var frustumLeft = leftPlane.normal;

        // 4.
        var halfDiameter = goDiameter / 2f;
        return borderPoint + frustumLeft * halfDiameter;
    }
    public static Vector3 GetPointRightOfCamera(Camera camera, float distance, float goDiameter)
    {
        var ray = camera.ScreenPointToRay(new Vector3(camera.pixelWidth - 1, camera.pixelHeight / 2f, 0));
        var borderPoint = ray.GetPoint(distance);

        var rightPlane = GeometryUtility.CalculateFrustumPlanes(camera)[1];
        var frustumRight = rightPlane.normal;

        var halfDiameter = goDiameter / 2f;
        return borderPoint + frustumRight * halfDiameter;
    }
}
\$\endgroup\$
1
\$\begingroup\$

You could do that using Camera.ScreenPointToRay() like this:

  1. Get the camera ray of the left/right most edge at the desired height.
  2. Then from that ray, take the point at the desired distance.
  3. Get the plane normal for the left/right camera frustum plane
  4. From that point, move to the left/right by half the renderer diameter.

If you place your object that that end point, it is just out of view, at a desired distance and near the edge of your camera frustum.

Code example:

public static Vector3 PlaceOutsideFrustum(this Camera camera, Bounds bounds, float distance, bool left)
{
    var frustumPlanes = GeometryUtility.CalculateFrustumPlanes(camera);
    Ray ray;
    Plane plane;

    if (left)
    {
        // 1.
        ray = camera.ScreenPointToRay(new Vector3(0, camera.pixelHeight / 2f, 0));
        plane = frustumPlanes[0];
    }
    else
    {
        ray = camera.ScreenPointToRay(new Vector3(camera.pixelWidth - 1, camera.pixelHeight / 2f, 0));
        plane = frustumPlanes[1];
    }

    // 2.
    var borderPoint = ray.GetPoint(distance);

    // 3.
    var frustumOutside = -plane.normal;

    // 4.
    var halfDiameter = bounds.size.magnitude / 2f;
    return borderPoint + frustumOutside * halfDiameter;
}

Usage example:

[RequireComponent(typeof(Renderer))]
public class PlaceOutsideCamera : MonoBehaviour
{
    public bool left = true;
    public float distance = 20f;
    public Camera camera;

    void OnEnable()
    {
        if (camera == null)
        {
            return;
        }

        var myRenderer = this.GetComponent<Renderer>();
        var bounds = myRenderer.bounds;
        this.transform.position = camera.PlaceOutsideFrustum(bounds, distance, left);
    }
}

Note:
Keep in mind, the goDiameter is the magnitude of the object renderer (mesh diameter in world space).
Also the object won't be directly touching the border of the camera frustum, as I approximated the mesh to a sphere by using the goDiameter.

If you need more precision, you can still use the same approach and replace the diameter offset calculation with your own.

Note 2:
The distance is not exact.
Because of the offset along the frustum left/right vector, the distance between the camera and the object is slightly larger than the distance passed to the method.
If that is a problem, one can fix that by normalizing the direction vector and scaling it to the desired length:

[...]
var position = camera.PlaceOutsideFrustum(bounds, distance, true);
var direction = (position - camera.transform.position).normalized;
this.transform.position = camera.transform.position + direction * distance;
\$\endgroup\$
  • \$\begingroup\$ How would I get the mesh's diameter? \$\endgroup\$ – tmighty Aug 15 at 15:47
  • \$\begingroup\$ I'm asking because unfortunately your approach doesn't work for me, and I'm trying to investigate where I may have gone wrong. \$\endgroup\$ – tmighty Aug 15 at 15:57
  • \$\begingroup\$ The easiest way would be to use the renderer.bounds. Then just take bounds.size.magnitude / 2 to get the half diameter. I couldn't test my approach above, as I'm not around my workplace atm. So please let me know if that helped or not. Might need to recheck the code. \$\endgroup\$ – Chillersanim Aug 15 at 16:21
  • \$\begingroup\$ I am using Bounds. Could you please write out the magnitude / 2 code as I'm not perfectly sure how that should look? Thank you. \$\endgroup\$ – tmighty Aug 15 at 16:53
  • \$\begingroup\$ Actually it shouldnt be divided by two, the method requires the full diameter. bounds.size.magnitude gives you the diameter. You pass this diameter to the method as goDiameter and it should work. \$\endgroup\$ – Chillersanim Aug 15 at 19:49
0
\$\begingroup\$

You would need to place the object just outside the camera's view frustum.

There are many ways to get frustum info. You can see here for one method with the Unity API.

\$\endgroup\$
  • \$\begingroup\$ I have tried that, and the object was far away from where I thought it would be. Could you perhaps be some more distinctive in your answer? \$\endgroup\$ – tmighty Aug 14 at 22:44
  • \$\begingroup\$ Can you show what you tried? \$\endgroup\$ – Ryan Aug 14 at 22:49
  • \$\begingroup\$ Vector3[] frustumCorners = new Vector3[4]; Camera.main.CalculateFrustumCorners(new Rect(0, 0, 1, 1), Camera.main.farClipPlane, Camera.MonoOrStereoscopicEye.Mono, frustumCorners); Vector2 worldSpaceCorner = Camera.main.transform.TransformVector(frustumCorners[0]); TheExtraTable.transform.position = worldSpaceCorner; I expected the worldspacecorner to be around X = 2, but it's -1000. \$\endgroup\$ – tmighty Aug 14 at 22:51
  • \$\begingroup\$ It could be because you are using the far clip plane distance (Camera.main.farClipPlane) as depth. If you try Camera.main.nearClipPlane is that closer to what you expect? This depth should be near the camera's position. \$\endgroup\$ – Ryan Aug 14 at 22:57
  • \$\begingroup\$ Yes, nearClipPlane is much closer with X being 0.3f, but still not what I would consider "expected". \$\endgroup\$ – tmighty Aug 14 at 23:02
0
\$\begingroup\$

You could add an "ObjectSpawner" game object as a child of your camera and place it slightly outside of the field of view of the camera. When the camera moves or rotates, so will your object spawner.

The object spawner would be pretty standard: Instantiate a prefab at its own world position and with its own world rotation.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.