How to zoom in/out camera based on player position on screen while keeping all objects in view?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By rstar

I want to make a camera system which allow for larger area, while keeping all objects in view and also want to do camera zoom in and out depending on player proximity.

Here is reference link of what i want to achieve.

https://nielson.io/2014/03/making-a-target-tracking-orthographic-camera-in-unity/

:bust_in_silhouette: Reply From: MysteryGM

The main problem with this kind of camera is that it loops every object, this can be very slow; if there are many objects.

Godot 3.1 Alpha3 (Could also work on older versions) File: MGMZoomCam.zip - Google Drive

In Some ways the Godot version is easier, in other ways it is more difficult:
This script must be attached to a node, and will track the children of the node.
A Camera2D is needed.

extends Node2D
#Godot uses pixels, so this is a percentage 4 = 4%
export var PaddingPercent = 4

func CalculateBox(InScreenSize):
	#infinity for the min max formulas to work
	var MinX = INF
	var MaxX = -INF
	var MinY = INF
	var MaxY = -INF
	#The way this works is it keeps the data from the nodes with the lowest -x,-y and highest x,y
	for eachChild in self.get_children():
		#Will only work with 2D, 3D needs transform.origin
		var pos = eachChild.position
		
		MinX = min(MinX,pos.x) # if pos.x is less than infinty keep it
		MaxX = max(MaxX,pos.x) # if pos.x is more than negative infinty keep it
		
		MinY = min(MinY,pos.y) # the next pass it compares the old kept value with the new
		MaxY = max(MaxY,pos.y) # keeping the most relavent number for that corner
	
	#Because Godot uses pixels we have to correct it
	var CorrectPixel =(InScreenSize /100) * PaddingPercent
	
	#Godot doesn't have a MinMaxRect but we can use a list
	var FourPointList = [
	MinX - CorrectPixel.y , 
	MaxX + CorrectPixel.y , 
	MinY - CorrectPixel.y , 
	MaxY + CorrectPixel.y ]
	#This will return a Rect2
	return Rect2From4PointList(FourPointList)
	
	
#Special function for making a rect2 from the list
func Rect2From4PointList(InList):
	#Formula AX+BX/2 AY+BY/2 
	var Center = Vector2( ((InList[0] + InList[1]) /2), ((InList[3] + InList[2]) /2) )
	#Formula BX-AX BY-AY 
	var Size = Vector2( (InList[1] -InList[0]), (InList[3] - InList[2]) )
	return Rect2(Center,Size)
	
	
func _process(delta):
	#You must have a camera 2D for this to work
	var ActiveCamera = get_node("../Camera2D") #Use the path to your camera
	var ScreenSize = self.get_viewport().size
	
	#This function will have to update every frame
	var CustomRect2 = CalculateBox(ScreenSize)
	
	var ZoomRatio = max(CustomRect2.size.x/ ScreenSize.x ,\
	 CustomRect2.size.y/ ScreenSize.y)
	
	ActiveCamera.offset = CustomRect2.position
	#ZoomRatio is a scalar so we need to turn it into a vector
	ActiveCamera.zoom = Vector2(1,1) * ZoomRatio
	

I forgot. If you want to limit the zoom you only need to adjust the ZoomRatio at the end, like this:

ActiveCamera.zoom = Vector2(1,1) * max(ZoomRatio, 0.5)

Now it will zoom in, to only half the normal size.
You can set a max like this:

ActiveCamera.zoom = Vector2(1,1) * min(ZoomRatio, 2)

To Zoom out to only twice the normal size, or if you want both you can use clamp:

ActiveCamera.zoom = Vector2(1,1) * clamp(ZoomRatio, 0.5, 2.0)

All of this code relies on min and max functions.

MysteryGM | 2018-12-22 03:39

Thanks for the answer, can you tell me how we can adjust zoom in/out speed smoothly?

rstar | 2018-12-22 06:35

can you tell me how we can adjust zoom in/out speed smoothly?

In theory as long as all your objects move smoothly then the zoom should be smooth.
But if you want to double check:

At the top where you declare the variables for the script put this:

export var Smooth = 0.01

This value tells the lerp how smooth it should be. Smaller is smoother. 0 will stop the zoom completely, while 1 is no smooth.

At the end you want this:

ActiveCamera.zoom = ActiveCamera.zoom.linear_interpolate( (Vector2(1,1) * ZoomRatio), Smooth)

This line tells it to lerp between the last known zoom and the newest zoom. Using Smooth as the time factor.

if you then also want to clamp it you should do it something like this:

var ClampedZoom =  Vector2(1,1) * clamp(ZoomRatio, 0.5, 2.0)
ActiveCamera.zoom = ActiveCamera.zoom.linear_interpolate( ClampedZoom, Smooth)

MysteryGM | 2018-12-22 10:13

@MysteryGM, this is working great as expected but when camera zoom out all objects are on screen looks small, no doubt this is expected behaviour but i don’t want to zoom out my player after certain level of camera zoom out so it can be visible to user and rest objects will zoom out as its happening now. How to achieve this? Can you plz help me on this? Thank you very much for your accurate answer.

rstar | 2019-01-09 05:52

Any words on this?

rstar | 2019-01-12 06:18