Prevent the camera from leaving the terrain

Viewed 95

The camera moves behind the Before - this is the GameObject.

public Transform Before, Camera;
public static volatile bool IsMove = false;
private float Speed = 0f;
private void FixedUpdate() {
    Speed = Rb.velocity.magnitude / 0.5f;
    IsMove = 1 > Speed;
    Camera.position = Vector3.Lerp(Camera.position, Before.position, (Speed <= 0 ? 11 : Speed) * Time.fixedDeltaTime);
}

Everything works fine, except that I can't figure out how to calculate that the camera has approached the boundary of the tirrane and there is no need to move it any further?

UPD:
I tried to do it like this:

if (!Physics.Raycast(cam.ViewportPointToRay(new Vector3(1, 0, 0)), cam.farClipPlane, terrainLayer)) {
    camera.position = new Vector3(oldPosition.x, camera.position.y, oldPosition.z);
    print("Right");
} else if (!Physics.Raycast(cam.ViewportPointToRay(new Vector3(0, 1, 0)), cam.farClipPlane, terrainLayer)) {
    camera.position = new Vector3(oldPosition.x, camera.position.y, oldPosition.z);
    print("Left");
}

But it was possible, as you can see, only 2 sides, but with the upper and lower parts, it will not work out in any way.

I need the camera to move the object only if it can be moved.

That is, if we move the object to the right side:

enter image description here

Then we can move along the y axis, if we have not reached the end point y. It needs to work automatically.

3 Answers

A simple idea, do raycast on each corner of the camera with the terrain, if failed, reject the movement.

Camera cam;
Transform camera;
var oldPosition = cameraTransform.position;
cameraTransform.position = newPosition;

int terrainLayer = LayerMask.GetMask("Terrain");

if (!Physics.Raycast(cam.ViewportPointToRay(new Vector3(0, 0, 0)), cam.farClipPlane, terrainLayer))
    camera.position = oldPosition ;
else if (!Physics.Raycast(cam.ViewportPointToRay(new Vector3(0, 1, 0)), cam.farClipPlane, terrainLayer))
    camera.position = oldPosition ;
else if (!Physics.Raycast(cam.ViewportPointToRay(new Vector3(1, 0, 0)), cam.farClipPlane, terrainLayer))
    camera.position = oldPosition ;
else if (!Physics.Raycast(cam.ViewportPointToRay(new Vector3(1, 1, 0)), cam.farClipPlane, terrainLayer))
    camera.position = oldPosition ;

Add a new answer to answer the updated example.

Add a helper method to simplify the code.

bool Raycast(float u, float v)
{
    return Physics.Raycast(cam.ViewportPointToRay(new Vector3(u, v, 0)), cam.farClipPlane, terrainLayer);
}

Still cast rays on the 4 corners.

int flag = 0;
if(Raycast(0, 1))
    flag |= 8;
if(Raycast(0, 1))
    flag |= 4;
if(Raycast(0, 1))
    flag |= 2;
if(Raycast(0, 1))
    flag |= 1;

Now we check which edge goes outside the rectangle, then don't move the camera towards that direction.

var delta = newPosition - oldPosition;
if(flag > 0)
{
   switch(flag)
   {
       case 5: //left
       case 9: //right
           delta.x = 0f;
           break;
       case 3: //up
       case 10: //down
           delta.y = 0f;
           break;
       default: //more than 1 edge
           delta.x = delta.y = 0f;
           break;
   }
   camera.position = oldPosition + delta;
}

I managed to do it like this:

if (!Physics.Raycast(cam.ViewportPointToRay(new Vector3(1, 1, 0)), cam.farClipPlane, terrainLayer)) {
    float x = Сamera.position.x;
    if (x <= oldPosition.x) {
        x = oldPosition.x;
    }
    Сamera.position = new Vector3(x, oldPosition.y, oldPosition.z);
} else if (!Physics.Raycast(cam.ViewportPointToRay(new Vector3(0, 0, 0)), cam.farClipPlane, terrainLayer)) {
    float x = Сamera.position.x;
    if (x >= oldPosition.x) {
        x = oldPosition.x;
    }
    Сamera.position = new Vector3(x, oldPosition.y, oldPosition.z);
}
if (!Physics.Raycast(cam.ViewportPointToRay(new Vector3(1, 0, 0)), cam.farClipPlane, terrainLayer)) {
    Сamera.position = new Vector3(oldPosition.x, oldPosition.y, Сamera.position.z);
} else if (!Physics.Raycast(cam.ViewportPointToRay(new Vector3(0, 1, 0)), cam.farClipPlane, terrainLayer)) {
    Сamera.position = new Vector3(oldPosition.x, oldPosition.y, Сamera.position.z);
}

It only works a little crookedly, sometimes there is a light behind the Terrain, but this is sometimes, perhaps because the speed of the object is too high and it breaks through because of this.
Or maybe not, I don't even know what it is yet.
I just don't know how else to do it.
I would be glad of any help, if there is a better and more reliable option, so that the camera goes strictly along the Terrain and does not go anywhere further.

Related