Keep object in camera view

Started by
31 comments, last by davejones 5 years, 8 months ago

I am using the rts cam script and was wondering if it was possible to stop the camera from going out of sight of an object. This set up is being developed in unity and is for a 3D game.  The camera is being used to rotate, pan and zoom in/around an object. At the moment I can move out of sight of the object. Is there a way to block the camera from going out of sight of the object? I have seen multiple ways to keep an object in sight through the camera, however I am unsure which route to take. 

I have attached the script being used below. 


public class RTSCam : MonoBehaviour {

	private float dist;
	
	private float orbitSpeedX;
	private float orbitSpeedY;
	private float zoomSpeed;
	
	public float rotXSpeedModifier=0.25f;
	public float rotYSpeedModifier=0.25f;
	public float zoomSpeedModifier=5;
	
	public float minRotX=-20;
	public float maxRotX=70;
	
	//public float minRotY=45;
	//public float maxRotY=315;
	
	public float minZoom=3;
	public float maxZoom=15;
	
	public float panSpeedModifier=1f;
	
	// Use this for initialization
	void Start () {
		dist=transform.localPosition.z;
		
		DemoSceneUI.SetSceneTitle("RTS camera, the camera will orbit around a pivot point but the rotation in z-axis is locked");
		
		string instInfo="";
			instInfo+="- swipe or drag on screen to rotate the camera\n";
			instInfo+="- pinch or using mouse wheel to zoom in/out\n";
			instInfo+="- swipe or drag on screen with 2 fingers to move around\n";
			instInfo+="- single finger interaction can be simulate using left mosue button\n";
			instInfo+="- two fingers interacion can be simulate using right mouse button";
		DemoSceneUI.SetSceneInstruction(instInfo);
	}
	
	void OnEnable(){
		IT_Gesture.onDraggingE += OnDragging;
		IT_Gesture.onMFDraggingE += OnMFDragging;
		
		IT_Gesture.onPinchE += OnPinch;
		
		orbitSpeedX=0;
		orbitSpeedY=0;
		zoomSpeed=0;
	}
	
	void OnDisable(){
		IT_Gesture.onDraggingE -= OnDragging;
		IT_Gesture.onMFDraggingE -= OnMFDragging;
		
		IT_Gesture.onPinchE -= OnPinch;
	}

	
	// Update is called once per frame
	void Update () {
		//get the current rotation
		float x=transform.parent.rotation.eulerAngles.x;
		float y=transform.parent.rotation.eulerAngles.y;
		
		//make sure x is between -180 to 180 so we can clamp it propery later
		if(x>180) x-=360;
		
		//calculate the x and y rotation
		//Quaternion rotationY=Quaternion.Euler(0, Mathf.Clamp(y+orbitSpeedY, minRotY, maxRotY), 0);
		Quaternion rotationY=Quaternion.Euler(0, y+orbitSpeedY, 0);
		Quaternion rotationX=Quaternion.Euler(Mathf.Clamp(x+orbitSpeedX, minRotX, maxRotX), 0, 0);
		
		//apply the rotation
		transform.parent.rotation=rotationY*rotationX;
		
		//calculate the zoom and apply it
		dist+=Time.deltaTime*zoomSpeed*0.01f;
		dist=Mathf.Clamp(dist, -maxZoom, -minZoom);
		transform.localPosition=new Vector3(0, 0, dist);
		
		//reduce all the speed
		orbitSpeedX*=(1-Time.deltaTime*12);
		orbitSpeedY*=(1-Time.deltaTime*3);
		zoomSpeed*=(1-Time.deltaTime*4);
		
		//use mouse scroll wheel to simulate pinch, sorry I sort of cheated here
		zoomSpeed+=Input.GetAxis("Mouse ScrollWheel")*500*zoomSpeedModifier;
	}
	
	//called when one finger drag are detected
	void OnDragging(DragInfo dragInfo){
		//if the drag is perform using mouse2, use it as a two finger drag
		if(dragInfo.isMouse && dragInfo.index==1) OnMFDragging(dragInfo);
		//else perform normal orbiting
		else{
			//apply the DPI scaling
			dragInfo.delta/=IT_Gesture.GetDPIFactor();
			//vertical movement is corresponded to rotation in x-axis
			orbitSpeedX=-dragInfo.delta.y*rotXSpeedModifier;
			//horizontal movement is corresponded to rotation in y-axis
			orbitSpeedY=dragInfo.delta.x*rotYSpeedModifier;
		}
	}
	
	//called when pinch is detected
	void OnPinch(PinchInfo pinfo){
		zoomSpeed-=pinfo.magnitude*zoomSpeedModifier/IT_Gesture.GetDPIFactor();
	}
	
	//called when a dual finger or a right mouse drag is detected
	void OnMFDragging(DragInfo dragInfo){
		//apply the DPI scaling
		dragInfo.delta/=IT_Gesture.GetDPIFactor();
		
		//make a new direction, pointing horizontally at the direction of the camera y-rotation
		Quaternion direction=Quaternion.Euler(0, transform.parent.rotation.eulerAngles.y, 0);
		
		//calculate forward movement based on vertical input
		Vector3 moveDirZ=transform.parent.InverseTransformDirection(direction*Vector3.forward*-dragInfo.delta.y);
		//calculate sideway movement base on horizontal input
		Vector3 moveDirX=transform.parent.InverseTransformDirection(direction*Vector3.right*-dragInfo.delta.x);
		
		//move the cmera 
		transform.parent.Translate(moveDirZ * panSpeedModifier * Time.deltaTime);
		transform.parent.Translate(moveDirX * panSpeedModifier * Time.deltaTime);
	}
	
	
	
	//for the camera to auto rotate and focus on a predefined position
	/*
	public float targetRotX;
	public float targetRotY;
	public float targetRotZ;
	public Vector3 targetPos;
	public float targetZoom;
	
	IEnumerator LerpToPoint(){
		Quaternion startRot=transform.parent.rotation;
		Quaternion endRot=Quaternion.Euler(targetRotX, targetRotY, targetRotZ);
		
		Vector3 startPos=transform.parent.position;
		Vector3 startZoom=transform.localPosition;
		
		float duration=0;
		while(duration<1){
			transform.parent.rotation=Quaternion.Lerp(startRot, endRot, duration);
			transform.parent.position=Vector3.Lerp(startPos, targetPos, duration);
			transform.localPosition=Vector3.Lerp(startZoom, new Vector3(0, 0, -targetZoom), duration);
			duration+=Time.deltaTime;
			yield return null;
		}
		
		transform.parent.rotation=endRot;
		transform.parent.position=targetPos;
		transform.localPosition=new Vector3(0, 0, -targetZoom);
	}
	*/
	
	
	/*
	private bool instruction=false;
	void OnGUI(){
		string title="RTS camera, the camera will orbit around a pivot point but the rotation in z-axis is locked.";
		GUI.Label(new Rect(150, 10, 400, 60), title);
		
		if(!instruction){
			if(GUI.Button(new Rect(10, 55, 130, 35), "Instruction On")){
				instruction=true;
			}
		}
		else{
			if(GUI.Button(new Rect(10, 55, 130, 35), "Instruction Off")){
				instruction=false;
			}
			
			GUI.Box(new Rect(10, 100, 400, 100), "");
			
			string instInfo="";
			instInfo+="- swipe or drag on screen to rotate the camera\n";
			instInfo+="- pinch or using mouse wheel to zoom in/out\n";
			instInfo+="- swipe or drag on screen with 2 fingers to move around\n";
			instInfo+="- single finger interaction can be simulate using left mosue button\n";
			instInfo+="- two fingers interacion can be simulate using right mouse button";
			
			GUI.Label(new Rect(15, 105, 390, 90), instInfo);
		}
	}
	*/
	
}

  

 

Advertisement
5 minutes ago, davejones said:

is there a way to block the camera from going out of sight of the object?

Yes, you just need to define what "sight" is and what the rules are.

For example, if the object has to stay in the center of the view, you can just use a ray check to see that there isn't anything in between the camera and the object; then move the camera so there no longer is.

However if you mean the object needs to stay in view all the time, then you need to grab the rendering bounds:  https://docs.unity3d.com/ScriptReference/Renderer-bounds.html and keep the object inside; this will work kind of like reversed collision.

 

Can you describe how the camera is going out of sight?

5 minutes ago, Scouting Ninja said:

Yes, you just need to define what "sight" is and what the rules are.

For example, if the object has to stay in the center of the view, you can just use a ray check to see that there isn't anything in between the camera and the object; then move the camera so there no longer is.

However if you mean the object needs to stay in view all the time, then you need to grab the rendering bounds:  https://docs.unity3d.com/ScriptReference/Renderer-bounds.html and keep the object inside; this will work kind of like reversed collision.

 

Can you describe how the camera is going out of sight?

When I say going out of sight the target is a 3D mesh. So when I am rotating/panning, the camera can move completely out of sight of the mesh. 

2 minutes ago, davejones said:

the camera can move completely out of sight of the mesh. 

Yes, but how does it go out of sight. For example if you rotate around the object, does a object obscure your view, or does your camera move freely like a flying camera?

1 minute ago, Scouting Ninja said:

Yes, but how does it go out of sight. For example if you rotate around the object, does a object obscure your view, or does your camera move freely like a flying camera?

The camera moves freely in that I can move out of sight of the mesh easily. 

OK, if you want to keep flying freely then you just need to keep the camera rotated towards the object. To do this you first calculate offset.

Offset = PointB - Point A. Offset is extremely useful because it gives both the direction and distance to a object. The problem is you have to "extract" the rotation and you do this by removing the distance.

Rotation = OffsetVector / magnitude. In Unity we can just use Vector.normalize  Except unity uses Quaternions so there is one more step, you need to convert your vector rotation to a Quaternions rotation.

Vector rotation to Quaternions = Quaternion.LookRotation(Vector, a up vector); Using these 3 steps your camera will be "locked" to a target.


Working Unity look at target script under the spoiler:

Spoiler

 



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

public class UnityLookAt : MonoBehaviour {

    public GameObject targetObject;

    Vector3 offset= Vector3.zero;
    Vector3 vecRot = Vector3.zero;

    // Use this for initialization
    void Start () {
	}
	
	// Update is called once per frame
	void Update () {
        offset = targetObject.transform.position - this.transform.position;
        vecRot = offset.normalized;

        this.transform.rotation = Quaternion.LookRotation(vecRot, Vector3.up);
    }
}

Note, because the camera is not a 6DOF camera, we lock the look axis to the global up. If it was a missile or other free moving object we would use the local up direction.

 

This is Linear Algebra, it is what is used to make games. It is easy to learn so don't feel demotivated.

3 minutes ago, Scouting Ninja said:

OK, if you want to keep flying freely then you just need to keep the camera rotated towards the object. To do this you first calculate offset.

Offset = PointB - Point A. Offset is extremely useful because it gives both the direction and distance to a object. The problem is you have to "extract" the rotation and you do this by removing the distance.

Rotation = OffsetVector / magnitude. In Unity we can just use Vector.normalize  Except unity uses Quaternions so there is one more step, you need to convert your vector rotation to a Quaternions rotation.

Vector rotation to Quaternions = Quaternion.LookRotation(Vector, a up vector); Using these 3 steps your camera will be "locked" to a target.


Working Unity look at target script under the spoiler:

  Hide contents

 




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

public class UnityLookAt : MonoBehaviour {

    public GameObject targetObject;

    Vector3 offset= Vector3.zero;
    Vector3 vecRot = Vector3.zero;

    // Use this for initialization
    void Start () {
	}
	
	// Update is called once per frame
	void Update () {
        offset = targetObject.transform.position - this.transform.position;
        vecRot = offset.normalized;

        this.transform.rotation = Quaternion.LookRotation(vecRot, Vector3.up);
    }
}

Note, because the camera is not a 6DOF camera, we lock the look axis to the global up. If it was a missile or other free moving object we would use the local up direction.

 

This is Linear Algebra, it is what is used to make games.

Thanks for the reply. I have added this script to the camera and I can still pan out of the objects sight. The target gameobject I have used is located in the centre of the 3D mesh. 

2 hours ago, davejones said:

I have added this script to the camera and I can still pan out of the objects sight.

I don't understand what you mean by this. Panning means rotating the camera from it's own center point, there is no possible way in the universe to pan a camera and keep a object in focus.

Image result for camera panning

This is how pan works, you will see that panning will always result in the camera rotating away from a object. The code I gave you only looks at a target point.

To rotate around a fixed object you need to either learn Quaternions or 3D rotation matrices.

11 hours ago, Scouting Ninja said:

I don't understand what you mean by this. Panning means rotating the camera from it's own center point, there is no possible way in the universe to pan a camera and keep a object in focus.

Image result for camera panning

This is how pan works, you will see that panning will always result in the camera rotating away from a object. The code I gave you only looks at a target point.

To rotate around a fixed object you need to either learn Quaternions or 3D rotation matrices.

I am looking into keeping the panning within a bounded area so that the camera can't be panned out of sight of the object. Effectively use collisions. So when the camera is being panned it can only move so far until it is blocked. 


     private void LateUpdate()
        {
            if (VisibleArea == null)
            {
                return;
            }

            Bounds b = VisibleArea.bounds;
            Vector3 world1 = _camera.ViewportToWorldPoint(Vector3.zero);
            Vector3 world2 = _camera.ViewportToWorldPoint(Vector3.one);
            Vector3 pos = transform.position;

            // move the camera so that the visible area is visible, if necessary

            // x axis
            if (world1.x > b.max.x)
            {
                pos.x -= (world1.x - b.max.x);
            }
            else if (world2.x < b.min.x)
            {
                pos.x += (b.min.x - world2.x);
            }

            // y axis
            if (world1.y > b.max.y)
            {
                pos.y -= (world1.y - b.max.y);
            }
            else if (world2.y < b.min.y)
            {
                pos.y += (b.min.y - world2.y);
            }

            transform.position = pos + (velocity * Time.deltaTime);
            velocity *= Dampening;
        }

Looking at implementing the code above. 



	private void LateUpdate()
	{
		if (VisibleArea == null)
		{
			return;
		}

		Bounds b = VisibleArea.bounds;
		Vector3 world1 = _camera.ViewportToWorldPoint(Vector3.zero);
		Vector3 world2 = _camera.ViewportToWorldPoint(Vector3.one);
		Vector3 pos = transform.position;

		// move the camera so that the visible area is visible, if necessary

		// x axis
		if (world1.x > b.max.x)
		{
			pos.x -= (world1.x - b.max.x);
		}
		else if (world2.x < b.min.x)
		{
			pos.x += (b.min.x - world2.x);
		}

		// y axis
		if (world1.y > b.max.y)
		{
			pos.y -= (world1.y - b.max.y);
		}
		else if (world2.y < b.min.y)
		{
			pos.y += (b.min.y - world2.y);
		}

		transform.position = pos + (velocity * Time.deltaTime);
		velocity *= Dampening;
	}
	
	

Looking at using this code as a way to keep a certain area visible at all times. 

This topic is closed to new replies.

Advertisement