Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon


28. July 2015

 

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 1920x1080 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 1600x900 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 1600x900 ( my native resolution ) and display resolution settings ignored.

image

 

Mode == 2d.  Image size is 1600x900.

image

 

Viewport stretch mode.  Image size is 800x600.

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 800x600 ( 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 379x124 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 1600x900, so everything looks fine with an HD resolution.  For example, here is the result rendered at 1280x720 full screen (but scaled down on the blog):

image

 

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

image

 

Ewwww…. ok… obviously not what we want.  The result of the resampling to fit a 640x480 image on a 1600x900 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 640x480 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:\Users\Mike\AppData\Roaming\Godot\app_userdata\Viewport 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 , ,

blog comments powered by Disqus

Month List

Popular Comments