Godot Engine Tutorial Part 13–Viewports and Cameras

We covered viewports a while back but now we are going to go into a bit more detail, as it’s an important subject.

There is a 1080p version of this tutorial available here.   Please note, the full screen portions didn’t display properly in the video.

One very important thing to understand is, the very root of your scene, the node that owns all of the nodes in a scene, is ultimately a viewport.  Consider this hierarchy:

image

In that node if you run the code:

func _ready():
   print(get_node("/root"))

You will see:

image

So, no matter how you create your scene, you will always have at least one viewport.  On the other hand, you can create more viewports within the scene as we will see later.

Full Screen Applications

It’s possible to set your application to run full screen, both using code or in the application settings.  To do it with code, create a new autoload script as described here.  This is a script, derived from node, that gets run automatically when your game launches.  Use the following code:

extends Node

func _ready():
   var root = get_node("/root")
   root.connect("size_changed",self,"resize")
   OS.set_window_fullscreen(true)
   set_process_input(true)
   
#Event called when viewport size changed  
func resize():
   var root = get_node("/root")
   var resolution = root.get_rect()
   print(resolution)
   
#Input handler, listen for ESC to exit app
func _input(event):
   if(event.is_pressed()):
      if(event.scancode == KEY_ESCAPE):
         get_tree().quit() 

Of course this code does a lot more than just set the application full screen.  Due to the app going full screen, it is no longer easy to close the window, so I’ve also wired in some code to handle shutting down if the user hits Esc.  There is also an event handler connected to fire when the resolution changes, we simple print the resolution to the console when it changes.

You can accomplish the exact same thing (much easier) using project settings, like so:

image

Please note there are two check boxes to enable!  First you need to enable the fullscreen setting, then you need to turn it on.

Viewport Scaling

Now to illustrate how resolution works in Godot, I’ve created a Sprite Node( not centered, at 0,0 ) using this image, which illustrates the various screen resolutions.

image

The actual image is 1920×1080 in size, so it should show us the results that various settings have on our game.  Here is a run, using default settings on my laptop which has a 1600×900 display. 

The following display settings are very important.

image

The following screenshots are all scaled down but maintain the aspect ratio of the source image.  The Viewport setting has a profound effect on the results.  The options are:

image

Viewport == disabled.  Resolution is 1600×900 ( my native resolution ) and display resolution settings ignored.

image

Mode == 2d.  Image size is 1600×900.

image

Viewport stretch mode.  Image size is 800×600.

image

It’s the ultimate results that make the difference.  When viewport is set to disabled, the width/height are ignored completely and the resolution of the device is used.  In viewport set to 2D, the width and height are used and the results are simply scaled up (or down) to match the resolution of the actual device.  While in Viewport, the results are actually scaled down to the resolution specified.  This means our actual render results are at 800×600 ( or whatever resolution you specified ).   You would generally use this last mode if you were trying to create a pixel perfect game, or if you are trying to render to a lower resolution to improved performance.  Keep in mind on most machines the results will look somewhat horrible.

Handling Aspect Ratios

Now this resizing works great when you are dealing with the same aspect ratios, but once they start changing, it has a much more pronounced effect.  For example, content designed for a 4:3 screen ( iPad ) will look horrible on a 16:9 screen ( Galaxy Note ) for example.  You also need to decide HOW you are going to deal with different aspect ratios.  This isn’t a new problem, people watching single def signals on HD displays have been dealing with this issue for years.

I created a new Sprite, this time using a sprite 379×124 pixels in size, like so:

image

Then automatically position it in the center of the viewport on load:

func _ready():
   self.set_pos(Vector2(get_viewport_rect().size.width/2,
                get_viewport_rect().size.height/2))

As mentioned earlier, my laptops native resolution is 1600×900, so everything looks fine with an HD resolution.  For example, here is the result rendered at 1280×720 full screen (but scaled down on the blog):

image

Looking good!  Now lets try 640×480, a not so HD aspect ratio:

image

Ewwww…. ok… obviously not what we want.  The result of the resampling to fit a 640×480 image on a 1600×900 screen as stretched our ship almost to the point of being unrecognizable.

You do however have options here, once again under display settings called stretch_aspect.

image

Lets see the result on our 640×480 scene:

ignore

image

keep

IMAG0363

keep_width

IMAG0364

keep_height

IMAG0365

You may notice the pictures are literally camera shots of my laptop.  This is because the screenshots don’t capture the black bar portions of the image.

Basically you can choose to simply rescale the aspect ratio, which causes the sprites to distort if the source and destination resolutions don’t have a similar aspect ratio.  Choosing Keep will cause it to keep the aspect ratio specified and generate black bars, either horizontally or vertically, whichever is needed.  You can also tell it to keep the height or the width aspect ratio.  The remaining dimension ( height if you chose Keep_width for example ) will then be scaled to fit, causing distortion in that direction.

Sub-Viewport

As mentioned earlier, the root node in the scene is always a viewport.  You can however create a viewport node within the scene or embedded within another node.

Consider this hierarchy of Nodes for example:

image

In the Editor it looks like a complete mess:

image

But when you run it, you can immediately see the results:

image

The nodes added to the child viewport are positioned relative to, and rendered within that viewport.

Cameras

The Camera2D class in Godot is mostly just responsible for manipulating the transform of the viewport.  A Camera automatically applies itself to the closest viewport above it in the node hierarchy, and if there isn’t one, it affects the root node instead.  Only one camera can be active at a time per viewport.

Otherwise using a camera is extremely simple.  Consider a scene like this that extends beyond the viewport:

image

Simply drop a Camera2D node into the scene:

image

Set Current To On

image

And your view will automatically update to represent the camera’s position

image

Taking a Screen Shot

When running full screen, capturing a screen shot can become a bit tricky.  I got around it by handling the logic in code.  If you are interested, here is how I captured a screenshot using Godot:

   if(event.scancode == KEY_SPACE):
         print("Screenshot")
         get_viewport().queue_screen_capture()
         yield(get_tree(), "idle_frame")
         yield(get_tree(), "idle_frame")
         var screenshot = get_viewport().get_screen_capture()
         screenshot.save_png("user://screenshot.png")

The location of “user://” is going to change from platform to platform.  On Windows 8.1 the screenshot was located at C:UsersMikeAppDataRoamingGodotapp_userdataViewport on my computer.  On Linux, check for a directory named .godot in your home directory.

The command queue_screen_capture() doesn’t happen immediately.  This is why we yeild two frames before calling get_screen_capture(), which will have the results of queue_screen_capture() or return an empty image if it hasn’t occurred yet.

The Video

Programming Godot Tutorial 2D


Scroll to Top