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


9. July 2015

 

In this part we are going to explore using Particles in the Godot Engine.  Particles are generally sprites, either provided by you or generated programmatically, that are controlled by a unified system.  Think of a rain storm, each drop of rain would represent a particle, but the entire storm itself would be the particle system.  You would not control each drop individually, instead you would simply say “rain here” and the system takes care of the rest.  Particles are often used for special effects such as smoke, fire or sparks.  The Godot game engine makes working with particles quite simple.

 

There is an HD video version of this tutorial available here or embedded below.

 

This particular tutorial isn’t going to go into a great deal of detail over the effects of each setting, as there is already an excellent illustrated guide right here.  Instead I will focus on using particles in a hands example.  As I mentioned earlier, particle systems are often used to create fire effects, and that’s exactly what we are going to do here, create a flaming/smoking torch.

 

Creating a particle system is as simple as creating a Particles2D node:

image

Creating one will create a simple system for you:

part1

 

As always, the properties are controlled in the Inspector.  In this case we are creating 32 particles with a lifespan of 2 seconds aimed down and affected by gravity:

image

 

Now let’s suit it to our needs.  First we want to change the direction of our particles to up instead of down. This is set by setting the direction property, this is a value in degrees for the particles to be emitted in.

image

 

Here is the result:

part2

Next, since this is a torch, we don’t want the particles to be affected by gravity.  Under Params, simply set Gravity Strength to 0:

image

And the result:

part3

Now white dots are exactly convincing flames… so lets add a bit of color.  This can be done using Color Phases.  These are the colors the a particle will go through during it’s lifetime.  For a torch, we will start with a brilliant white, then orange and finally red, like so:

image

Be sure to set the Count to 3.  You can have up to 4 phases if needed.  Now our results look like:

part4

A bit better.  Now we want to work on the size a bit.  Let’s start our particles off bigger and shrink as they move away from the source of the flame.

image

Resulting in:

part5

 

Finally we slow it down slightly and decrease the spread:

image

 

And voila:

part6

 

A fairly passable torch.  You could play with it a bit, use an image instead of a square particle, change the alpha value of each color phase or add another overlapping particle system to provide smoke.  Keep in mind though, more particles, more processing.

 

Here is a simple layer of smoke added as a separate particle system and the alpha lowered on the final two color phases:

part7

 

Particles are as much an art to create as a texture or 3D model.  Play around until you achieve the effect you want.  Be sure to read that link I post earlier for the effects various settings have on your particle system.  One other area I never touch on was randomization.  In addition to numerous settings for controlling how particles are created, you can also randomize each of those values so your particles end up being less consistent.

 

As mentioned earlier, a particle can also be created from a texture or series of textures.  To set a texture, simply set it’s texture property:

image

 

In this example I am going to use this spritesheet to create a flock of animated birds:

robincropped

 

Set H and V to correspond to the number of rows and columns in your TextureAtlas:

image

 

I am unsure of how to deal with TextureAtlases with empty squares, there doesn’t seem to be a way to set a total count, but I may have overlooked it.  Next you are going to want to specify the speed you want it to jump between frames of animation using Anim Speed Scale

image

I tweaked a few more settings:

image

And my final results are a fairly nice flock of birds:

part8

 

One other feature available is the particle attractor ParticleAttractor2D, which can be used to attract particles, to either fling them out the other side or absorb them.  Think of it like a black hole that either sucks in or spits out the particles in it’s radius of influence:

image

part9

Keep in mind that particles all have a lifespan, and once that lifespan has elapsed, it will fade away.

 

Particles provide a powerful way of implementing tons of graphically similar effects ( like fire, fog, flocking etc ) with a single controlling system.  They are as much art as programming though, so will take some time playing around to get the effect just right.

 

The Video

 

Programming


18. June 2015

 

So I decided to take a look at the process of extending the Godot Engine, and to my horror I discovered there is no solution file!  UGH…  Yeah, you can compile from the command line, but that’s not that pleasant of a development experience to an IDE warrior like myself.  I thought I found a solution that I posted about here and I almost did, but it still left me unable to debug and with mostly broken Intellisense support… so, yeah, not great.  Then I found this post, which although it didn’t work, it did fill in the missing pieces.  So, if you want to work with Godot using Visual Studio, here’s how you do it.

 

First you need to have a properly configured development environment… meaning Visual Studio, git and python, all in your PATH.  Next launch a Visual Studio command line, this guy…

image 

 

Make and change to the directory you want to install Godot.  Keep in mind this process will make a godot folder, so if you want c:\godot\ run the following from c:\.

 

git clone https://github.com/okamstudio/godot.git

This will download the latest source for Godot Engine.  Next we want to build it for the first time and generate a Visual Studio solution file.  cd into the Godot directory and run:

scons vsproj=yes platform=windows

If you get an error here, first be sure you are using a Visual Studio command prompt, next be certain you are in the correct directory.  Otherwise your computer should churn away for a few minutes while godot libraries and tools are built.

After several minutes, in an ideal world you should see:

image

 

This means Godot successfully built and it created your Visual Studio project files.  Woot.  Now time to get Visual Studio to actually work.

 

First in the root of your project directory ( C:\Godot\ in my case ), create a file named build.bat, with the following contents:

set vc_path=%1
call %vc_path% & scons platform=windows

 

Next load the generated sln file in Visual Studio.  Give it a minute or two to parse all the files.  You will notice massive amounts of Intellisense errors, don’t worry, we will fix those next.

In Solution Explorer, right click your Project ( not solution, Project! ) and select Properties.

image

Select VC++ Directories then double click Include Directories:

image

Append the following to the value in Include Directories:

$(ProjectDir);$(ProjectDir)/core;$(ProjectDir)/core/math;$(ProjectDir)/tools;$(ProjectDir)/drivers;$(ProjectDir)/platform/windows;

This adds the include directories Godot depends on.

 

Next click NMake on the left hand side.  We now want to replace the contents of Build Command Line and Output to:

image

Then click Apply then OK.

 

You should now be able to hit Build->Build Solution.  If all went right, you should see build progress in the Output panel:

image

 

You can now run and debug as normal, set breakpoints within the code, hit F5 and Godot editor will run.  Keep in mind, it’s the Godot Editor you are debugging, not the library, although since you have full source you should easily be able to step into Godot core code, you just may not be able to set breakpoints there.

 

You are now ready to extend or contribute to Godot using the full power and comfort of Visual Studio.

 

The following is a video showing exactly this process, just in case I missed a step.

 

Programming


16. June 2015

 

Now we are going to talk about two important concepts in AI development in 2D games, path following and navigation meshes.  Path following is exactly what you think it is, you create paths and follow them.  This is useful for creating predefined paths in your game.  When you are looking for a bit more dynamic path finding for your characters, Navigation Mesh ( or NavMesh ) come to the rescue.  A NavMesh is simply a polygon mesh that defines where a character can and cannot travel.

 

As always there is an HD video of this tutorial available here.

Let’s start with simple path following.  For both of these examples, we are going to want a simple level to navigate.  I am going to create one simply using a single sprite background that may look somewhat familiar…

image

 

So, we have a game canvas to work with, let’s get a character sprite to follow a predefined path.

 

Path2D and PathFollow2D

 

First we need to start off by creating and defining a path to follow.  Create a new Path2D node:

image

 

This will add additional editing tools to the 2D view:

image

 

Click the Add Point button and start drawing your path, like so:

image

 

Now add a PathFollow2D node, and a Sprite attached to that node, like so:

image

 

There are the following properties on the PathFollow2D node:

image

 

You may find that you start rotated for some reason.  The primary setting of concern though is the Offset property.  This is the distance along the path to travel, we will see it in action shortly.  The Loop value is also important, as this will cause the path to go back to offset 0 once it reaches the end and start the travel all over again.  Finally I clicked Rotate off, as I don’t want the sprite to rotate as it follows the path.

 

Now, create and add a script to your player sprite, like so:

extends Sprite


func _ready():
   set_fixed_process(true)

func _fixed_process(delta):
   get_parent().set_offset(get_parent().get_offset() + (50*delta))

 

This code simply gets the sprites parent ( the PathFollow2D Node ), and increments it’s offset by 50 pixels per second.  You can see the results below:

PathFollow

 

You could have course have controlled the offset value using keyframes and an animation player as described in the previous chapter.

 

So that’s how you can define movement across a predefined path… what about doing things a bit more dynamic?

 

Navigation2D and NavigationPolygon

 

Now let’s create a slightly different node hierarchy.  This time we need to create a Navigation2D Node, either as the root, or attached to the root of the scene.  I just made it the root node.  I also loaded in our level background sprite.  FYI, the sprite doesn’t have to be parented to the Navigation2D node.

image

 

Now we need to add a Nav Mesh to the scene, this is done by creating a NavigationPolygonInstance, as a child node of Navigation2D:

image

 

This changes the menu available in the 2D view again, now we can start drawing the NavMesh.  Start by outlining the entire level.  Keep in mind, the nav mesh is where the character can walk, not where they can’t, so make the outer bounds of your initial polygon the same as the furthest extent the character can walk.  To start, click the Pen icon.  One first click you will be presented this dialog:

image

 

Click create.  Then define the boundary polygon, like so:

image

 

Now using the Pen button again, start defining polygons around the areas the character cant travel.  This will cut those spaces out of the navigation polygon.  After some time, I ended up with something like this:

image

 

So we now have a NavMesh, let’s put it to use.  Godot is now able to calculate the most efficient path between two locations.

For debug reasons, I quickly import a TTF font, you can read this process in Chapter 5 on UI, Widgets and Themes.  Next attach a script to your Navigation2D node.  Then enter the following code:

extends Navigation2D
var path = []
var font = null
var drawTouch = false
var touchPos = Vector2(0,0)
var closestPos = Vector2(0,0)

func _ready():
   font = load("res://arial.fnt")
   set_process_input(true)

func _draw():
   if(path.size()):
      for i in range(path.size()):
         draw_string(font,Vector2(path[i].x,path[i].y - 20),str(i+1))
         draw_circle(path[i],10,Color(1,1,1))
      
      if(drawTouch):
         draw_circle(touchPos,10,Color(0,1,0))  
         draw_circle(closestPos,10,Color(0,1,0))
   

func _input(event):
   if(event.type == InputEvent.MOUSE_BUTTON):
      if(event.button_index == 1):
         if(path.size()):
            touchPos = Vector2(event.x,event.y)
            drawTouch = true
            closestPos = get_closest_point(touchPos)
            print("Drawing touch")
            update()
            
      if(event.button_index == 2):
         path = get_simple_path(get_node("Sprite").get_pos(),Vector2(
                event.x,event.y))
         update()

 

This code has two tasks.  First when the user clicks right, it calculates the closest path between the character sprite and the clicked location.  This is done using the critical function get_simple_path() which returns a Vector2Array of points between the two locations.  Once you’ve calculated at least one path ( the path array needs to be populated ), left clicking outside of the navmesh will then show two circles, one where you clicked, the other representing the closest navigable location, as returned by get_closest_point().

 

Here is our code in action:

PacNav

As you right click, a new path is established drawn in white dots.  Then left clicking marks the location of the click and the nearest walk-able location in the nav polygon.  You may notice the first left click resulted in it drawing a location to the left of the screen.  This is because my navmesh wasn’t water tight, lets look:

image

 

Although miniscule in size, this small spot of polygons is a valid path to the computer.  When setting your nav mesh’s up, be sure to make sure you don’t leave gaps like this!

 

There are a couple things you might notice.  The path returned is the minimum direct navigable line between two points.  It however does not take into account the size of the item you want to move.   This is logic that you need to provide yourself.  In the example of something like PacMan, you are probably better off using a cell based navigation system, based on an algorithm like a*star.  I really wish get_closest_path() allowed you to specify the radius of your sprites bounding circle to determine if the path is actually large enough to travel.  As it stands now, you are going to have to make areas that are too small for your sprite as completely filled.  This renders Navigation2D of little use to nodes of varying sizes.

 

Regardless of the limations, Navigation2D and Path2D provide a great template for 2D based AI development.

 

The Video

Programming


14. June 2015

 

As mentioned recently, I am in the process of compiling the Godot Game Engine Tutorial Series into an e-book format.  Today I just published the 10 chapter, 150 page first draft of the Godot Engine book.

 

BookCover

It can currently be downloaded by Patreon backers right here.

 

It is currently available in the following formats:

  • PDF
  • epub
  • mobi

 

Due to the large file size (@15mb) to install on a Kindle you will need to install via side-loading, the file is beyond the limits for emailing to Kindle.

 

Right now, this is mostly just a straight compilation of content available here on GameFromScratch.com.  I will need to do an editorial pass to make sure text makes sense in book format, as well as replacing now static animated gifs with more meaningful images.  If you prefer to read offline, wish to print or want to reader on an e-reader, this book should be perfect for you.

 

If you are interested in checking it out, Chapter 8: Using Tilemaps can be downloaded here. Of course, if you’ve already read the Godot tutorial series, this is going to be incredibly familiar.

Programming News


GFS On YouTube

See More Tutorials on DevGa.me!

Month List