+1 vote

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.


in Engine by (22 points)

1 Answer

+3 votes
Best answer

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: https://drive.google.com/open?id=1W4kh3_bCD6dl7h9Igwvk2JdJsJAWVQML

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
by (1,445 points)
selected by

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.

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

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, 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.

Any words on this?

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.