5

I'm doing this thing where an object is moving on a plane, and the camera is in the center of it. I got it so the camera rotates along with the mouse, and when the main camera sees the game object, it stops. So I was using the onbecamevisible() and onbecameinvisible() functions, but that applies to any camera, including the scene view. How do I make it so the objects stops when seen only by the main game camera?

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

public class cubeMove : MonoBehaviour,moveObject
{

Camera cam;
public Transform checkedObject;

void Start()
{
    cam = GetComponent<Camera>();
}



void Update()
{
    Vector3 viewPos = cam.WorldToViewportPoint(checkedObject.position);
    if ()
        move();
}

public void move()
{
    transform.Rotate(new Vector3(15, 30, 45) * Time.deltaTime);
}

}

Here's my Camera Script

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

public class Camera : MonoBehaviour {

public enum RotationAxes { MouseXAndY = 0, MouseX = 1, MouseY = 2 }
public RotationAxes axes = RotationAxes.MouseXAndY;
public float sensitivityX = 15F;
public float sensitivityY = 15F;

public float minimumX = -360F;
public float maximumX = 360F;

public float minimumY = -60F;
public float maximumY = 60F;

float rotationX = 0F;
float rotationY = 0F;

private List<float> rotArrayX = new List<float>();
float rotAverageX = 0F;

private List<float> rotArrayY = new List<float>();
float rotAverageY = 0F;

public float frameCounter = 20;

Quaternion originalRotation;

void Update()
{
    if (axes == RotationAxes.MouseXAndY)
    {
        rotAverageY = 0f;
        rotAverageX = 0f;

        rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
        rotationX += Input.GetAxis("Mouse X") * sensitivityX;

        rotArrayY.Add(rotationY);
        rotArrayX.Add(rotationX);

        if (rotArrayY.Count >= frameCounter)
        {
            rotArrayY.RemoveAt(0);
        }
        if (rotArrayX.Count >= frameCounter)
        {
            rotArrayX.RemoveAt(0);
        }

        for (int j = 0; j < rotArrayY.Count; j++)
        {
            rotAverageY += rotArrayY[j];
        }
        for (int i = 0; i < rotArrayX.Count; i++)
        {
            rotAverageX += rotArrayX[i];
        }

        rotAverageY /= rotArrayY.Count;
        rotAverageX /= rotArrayX.Count;

        rotAverageY = ClampAngle(rotAverageY, minimumY, maximumY);
        rotAverageX = ClampAngle(rotAverageX, minimumX, maximumX);

        Quaternion yQuaternion = Quaternion.AngleAxis(rotAverageY, Vector3.left);
        Quaternion xQuaternion = Quaternion.AngleAxis(rotAverageX, Vector3.up);

        transform.localRotation = originalRotation * xQuaternion * yQuaternion;
    }
    else if (axes == RotationAxes.MouseX)
    {
        rotAverageX = 0f;

        rotationX += Input.GetAxis("Mouse X") * sensitivityX;

        rotArrayX.Add(rotationX);

        if (rotArrayX.Count >= frameCounter)
        {
            rotArrayX.RemoveAt(0);
        }
        for (int i = 0; i < rotArrayX.Count; i++)
        {
            rotAverageX += rotArrayX[i];
        }
        rotAverageX /= rotArrayX.Count;

        rotAverageX = ClampAngle(rotAverageX, minimumX, maximumX);

        Quaternion xQuaternion = Quaternion.AngleAxis(rotAverageX, Vector3.up);
        transform.localRotation = originalRotation * xQuaternion;
    }
    else
    {
        rotAverageY = 0f;

        rotationY += Input.GetAxis("Mouse Y") * sensitivityY;

        rotArrayY.Add(rotationY);

        if (rotArrayY.Count >= frameCounter)
        {
            rotArrayY.RemoveAt(0);
        }
        for (int j = 0; j < rotArrayY.Count; j++)
        {
            rotAverageY += rotArrayY[j];
        }
        rotAverageY /= rotArrayY.Count;

        rotAverageY = ClampAngle(rotAverageY, minimumY, maximumY);

        Quaternion yQuaternion = Quaternion.AngleAxis(rotAverageY, Vector3.left);
        transform.localRotation = originalRotation * yQuaternion;
    }
}

void Start()
{
    Rigidbody rb = GetComponent<Rigidbody>();
    if (rb)
        rb.freezeRotation = true;
    originalRotation = transform.localRotation;
}

public static float ClampAngle(float angle, float min, float max)
{
    angle = angle % 360;
    if ((angle >= -360F) && (angle <= 360F))
    {
        if (angle < -360F)
        {
            angle += 360F;
        }
        if (angle > 360F)
        {
            angle -= 360F;
        }
    }
    return Mathf.Clamp(angle, min, max);
}

}

SphereMove

public class sphereMove : MonoBehaviour,moveObject {

public float delta = 1.5f;  
public float speed = 2.0f;
private Vector3 startPos;

public UnityEngine.Camera cam;
public Transform checkedObject;
bool isMoving;

void Start()
{
    isMoving = true;
    cam = Camera.main;

}

void Update()
{

    Vector3 viewPos = cam.WorldToViewportPoint(checkedObject.position);
    if (viewPos.x > 0 && viewPos.x <= 1 && viewPos.y >= 0 && viewPos.y <= 1 && viewPos.z > 0)
        isMoving = false;
    else
        isMoving = true;

    if (isMoving)
        move();
}
public void move()
{
    Vector3 v = startPos;
    v.x += delta * Mathf.Sin(Time.time * speed);
    transform.position = v;
}

}

user8048595
  • 119
  • 1
  • 2
  • 12
  • Are you considering your object is "seen" by the camera when in it is in the range of the camera or it is actually seen on the screen (that means not hidden by any other item)? – Izuka Sep 22 '17 at 12:45
  • for now, just whatever's on screen – user8048595 Sep 22 '17 at 12:47
  • You want to do something when an object is in the view of a camera, but which camera? The one you've made a class or the built-in Unity's Camera component? – mrogal.ski Sep 22 '17 at 13:03
  • the main camera in hierarchy I made a script for – user8048595 Sep 22 '17 at 13:05
  • @user8048595 As said, change `Camera cam` into `UnityEngine.Camera cam` to make it works. Or change the name of your Camera script. Because right now your script is trying to find `WorldToViewportPoint` into **your script**, and not Unity's one. – Izuka Sep 22 '17 at 13:09

2 Answers2

10

You can use the Camera.WorldToViewport function, using your Main Camera to call it and giving in parameter the position of the object your are checking. If the x and y coordinates values of the result vector are between 0 and 1 and the z value is superior to 0, it means the center of your object is seen by your camera.

UnityEngine.Camera cam;
bool isMoving;

void Start()
{
    cam = UnityEngine.Camera.main;
    isMoving = true;
}

void Update()
{
    Vector3 viewPos = cam.WorldToViewportPoint(checkedObject.position);
    if (viewPos.x >= 0 && viewPos.x <= 1 && viewPos.y >= 0 && viewPos.y <= 1 && viewPos.z > 0)
    {
        // Your object is in the range of the camera, you can apply your behaviour
        isMoving = false;
    }
    else
        isMoving = true;

    if(isMoving)
        Move();
}
Izuka
  • 2,572
  • 5
  • 29
  • 34
  • But another object can block the vie with this and the camera will still "see" it – Programmer Sep 22 '17 at 12:37
  • There's an error saying Camera does not contain a definition for WorldToViewportPoint. – user8048595 Sep 22 '17 at 12:48
  • @user8048595 Do you have a script called Camera? If that's the case, your program will use that script instead of the UnityEngine.Camera one, and thus you won't be able to find the function. If that's the case, just declare Camera as UnityEngine.Camera. – Izuka Sep 22 '17 at 12:54
  • 1
    Remove **",moveObject**" that is placed after `MonoBehaviour` I can't even tell why you have that there. Also, do **not** name your script `Camera`. Rename it both in the Editor and in Visual Studio. – Programmer Sep 22 '17 at 12:58
  • I was practicing strategy pattern. It's not important right now – user8048595 Sep 22 '17 at 13:00
  • I added the script, it says I need to add a camera component. Do I have to add a new camera for every moving object I want to stop? – user8048595 Sep 22 '17 at 13:20
  • @user8048595 Just set your `cam` variable to `Camera.main` in your Start function. You don't need to add a new camera nor anything, it will work with your main one as you asked. – Izuka Sep 22 '17 at 13:21
  • Ok. I think I got something. It moves when the camera sees it. I want to stop it when seen. Do I set move to Start() and then stop move in the if statement? How would I do that? – user8048595 Sep 22 '17 at 13:31
  • @user8048595 Add a boolean to your class, something like `isMoving`, and set it to true in your Start function. Then in your Update, check for the condition I did put in my answer, and set `isMoving` to false if that's the case. Then just put an if condition testing if `isMoving` is true, and if that's the case, call your `Move` function. – Izuka Sep 22 '17 at 13:36
  • if(ismoving==true) move(). Do I set that in update() at the end? Because it only works once. – user8048595 Sep 22 '17 at 13:46
  • @user8048595 I edited my answer to show you the idea. – Izuka Sep 22 '17 at 13:46
  • Sorry One more thing. I'm applying the same rules to a sphere thats move left to right, but I play and look away, the sphere ends up in the center of the plane where the camera is and starts moving when it should be against the wall moving. I think the code I applied to the cube has to do with that. – user8048595 Sep 22 '17 at 14:07
  • Another big problem with the `WorldToViewportPoint` is that the object may be **partially** visible by your camera and `WorldToViewportPoint` will indicate the object is outside the viewport.... – Hellium Sep 22 '17 at 17:23
  • @Hellium That's why I precised this solution would only work if the center of the object is seen by the camera. – Izuka Sep 22 '17 at 17:26
6

Methods suggested does not work in your case because you've made a class called Camera which has exactly the same name as Unity's Camera component.

I would suggest you use the approach from the community wiki. It's simple enough to understand and lets you check visibility for each camera.


You can create an extension class for the Camera component :

public static class CameraEx
{
    public static bool IsObjectVisible(this UnityEngine.Camera @this, Renderer renderer)
    {
        return GeometryUtility.TestPlanesAABB(GeometryUtility.CalculateFrustumPlanes(@this), renderer.bounds);
    }
}

The you can call that from each object separately like such :

// UnityEngine.Camera cam1;
// UnityEngine.Camera cam2;

void Update()
{
    bool isVisibleForCamera1 = cam1.IsObjectVisible(GetComponent<MeshRenderer>());
    bool isVisibleForCamera2 = cam2.IsObjectVisible(GetCoponent<SpriteRenderer>());
}

Here you can find whole documentation about GeometryUtility

mrogal.ski
  • 5,828
  • 1
  • 21
  • 30