Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
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


7. June 2015

 

In the previous tutorial we covered Sprite Animation, although to be honest it was more about creating animation ready sprites.  The actual way we performed animation wasn’t ideal.  Fortunately we are about to cover a way that is very much ideal… and capable of a great deal more than just animating sprites!

 

As always, there is an HD video of the this tutorial available right here or embedded below.  It’s important to have followed the previous tutorial, as we will be building directly on top of it.

 

Keyframes Explained

 

Before we get to far into this tutorial I think it’s pretty critical to cover a key concept in animation, keyframing.  Essentially you animate by setting a number of “key” frames in the animations timeline, then let the computer take care of the rest.  You can set a keyframe on just about any property available in Godot as we will soon see.  For example you can create a key on the position value of a sprite.  Then advance the timeline, set another key at a different position.  The computer will then interpolate the position over time between those two keys.  This interpolation between key frames is often referred to as a “tweening” as in “inbetween”.  Don’t worry, it will make a lot more sense when we jump in shortly.

 

AnimationPlayer

 

In the previous tutorial, we created a simple animation using code to increment the current frame at a fixed play rate.  Now we are going to accomplish the same thing using the built in animation system in Godot. 

Start by opening up the previous project and remove the code from our AnimatedSprite.  Now add a AnimationPlayer node under the root of your scene, like so:

image

 

With the AnimationPlayer selected, you will notice a new editor across the bottom of the 2D window:

image

 

This is your animation timeline control.  Let’s create a new animation named “walkcycle”

Click the New Animation icon

image

 

Name your animation and click Ok

image

 

Click the Edit icon

image

 

This will extend the animation options even more.

image

 

First let’s set the duration of our animation to 2 seconds:

image

 

You can then adjust the resolution of the animation timeline using the Zoom slider:

image

 

Here we’ve zoomed in slightly to show just over 2 seconds:

image

 

Now that we are in edit mode with our AnimationPlayer selected, in the 2D view, you will notice there are new options available across the top

image

 

This is a quick way to set keys for a nodes positioning information.  You can toggle on or off whether the key will store location, rotation and/or scale data.  You set a key by pressing the Key icon.  The first time you press the key icon you will be prompted if you want to create a new track of animation.

Select your sprite, make sure the timeline is at 0 and create a key. Advance the timeline to 2secconds, then move the sprite to the right slighly, then hit the key icon again to create another key frame.

g1

 

Press the play icon in the AnimationPlayer to see the animation you just created:

g2

 

Well that’s certainly movement, but pretty crap as far as animations go eh?  How about we add some frame animation to our mix.  Here is where you start to see the power of animation in Godot.

 

With the AnimationPlayer selected, rewind the timeline back to zero, make sure you select your AnimatedSprite, then in the Details panel you will notice that all of the properties have a little key beside them:

image

 

This is because you can keyframe just about any value in Godot.  We are now going to do it with the Frame value.  This is the value we programmatically increased to create our animation before.  Now we will instead do it using keyframes.  With the timeline at 0, set Frame to 0 then click the Key icon to it’s right.  Click Create when it prompts you if you wish to create a new track.  Then move the timeline to the end, increase Frame to the final frame (21 in my case), then click the Key again.  This will create a new track of animation:

image

 

You can see the different track names across the left.  The blue dots represent each key frame in the animation.  There is one final change we have to make.  Drop down the icon to the right of the animation track and change the type to Continuous, like so:

image

 

Now when you press play, you should see:

g3

 

Playing Your Animation

 

While your animation appears properly if you press Play in the AnimationPlayer interface, if you press Play on your game, nothing happens.  Why is this?

 

Well simply put, you need to start your animation.  There are two approaches to starting an animation.

 

Play Animation Automatically

You can set the animation to play automatically.  This means when the animation player is created it will automatically start the selected animation.  You can toggle if an animation will play automatically using this icon in the Animation Player controls.

image

 

Play an Animation Using Code

Of course, AnimationPlayer also has a programmatic interface.  The following code can be used from the scene root to play an animation:

extends Node

func _ready():
   get_node("AnimationPlayer").play("walkcycle")

 

Scripting the AnimationPlayer

 

Say you want to add a bit of logic to your keyframed animation…  with AnimationPlayer you have a couple options we can explore.

 

First there are the events that are built into the AnimationPlayer itself:

image

For simple actions like running a script when an animation changes or ends, using AnimationPlayer connections should be more than enough.

 

What about if you wanted to execute some code as part of your animation sequence?  Well that is possible too.  In your Animation editor window, click the Tracks button to add a new animation track:

image

 

Select Add Call Func Track:

image

 

Another track will appear in your animation.  Click the green + to add a new keyframe.

image

 

Now left click and drag the new key to about the halfway (1 second) mark.  Switch to edit mode by clicking the pen over a dot icon, then click your keyframe to edit it.  In the name field enter halfway.  This is the name of the method we are going to call:

g4

 

Add a method to your root scene named halfway:

extends Node

func _ready():
   get_node("AnimationPlayer").play("walkcycle")

func halfway():
   print("Halfway there")

 

When the keyframe is hit, the halfway method will be called.  Adding more function calls is as simple as adding more keys, or complete Call Func tracks.  As you may have noticed in the video above, you have the ability to pass parameters to the called function:

image

 

Finally, you can also access animations, tracks and even keys directly using code.  The following example changes the destination of our pos track.  This script was attached to the AnimationPlayer object:

extends AnimationPlayer


func _ready():
   var animation = self.get_animation("walkcycle")
   animation.track_set_key_value(animation.find_track("AnimatedSprite:transform/pos"),1,Vector2(400,400))

 

Now when you run the code you should see:

g5

 

The Video

Programming


3. June 2015

 

In this tutorial we are going to look at Sprite Animation in Godot Engine, specifically on using the AnimatedSprite class.  We are going to import and create a node that has multiple frames of animation, then look at some code to flip between frames.  In the immediately following tutorial, we will then cover a much better animation method using AnimationPlayer.

 

As always, there is an HD Video version of this tutorial available right here or embedded below.

 

Alright, let’s just right in with AnimatedSprite.

 

Sprite Animation

AnimatedSprite is a handy Node2D derived class that enables you to have a node with multiple SpriteFrames.  In plain English, this class enables us to have a sprite with multiple frames of animation. 

 

Speaking of frames of animation, this is the sequence of png images I am going to use for this example:

image

 

You can download the zip file containing these images here, or of course you can use whatever images you want.

 

Now we simply want to import them to our using the standard Import->2D Texture method.  Be aware, you can multi select in the Importer, so you can import the entire sequence in one go.  Assuming you’ve done it right, your FileSystem should look somewhat like:

image

 

Now add an AnimatedSprite node to your scene like so:

image

 

Now we add the frames to our AnimatedSprite by selecting Frame->New SpriteFrames

image

 

Now drop it down again and select Edit:

image

 

The 2D editor will now be replaced with the SpriteFrames editor.  Click the open icon:

image

 

Shift select all of the sprite frames and select OK

image

 

All of your sprites should now appear in the editor:

image

 

Now let’s add some code to flip through the frames of our AnimatedSprite.  Attach a script to the AnimatedSprite node, then use the following code:


extends AnimatedSprite

var tempElapsed = 0

func _ready():
   set_process(true)
   
func _process(delta):
   tempElapsed = tempElapsed + delta
   
   if(tempElapsed > 0.1):
      if(get_frame() == self.get_sprite_frames().get_frame_count()-1):
         set_frame(0)
      else:
         self.set_frame(get_frame() + 1)
      
      tempElapsed = 0
   
   print(str(get_frame() + 1))

The logic is pretty simple.  In our process tick we increment a variable tempElapsed, until 1/10th of a second has elapsed, at which point we move on to the next frame.  If we are at the last frame of our available animation, we then go back to the very first frame.

 

When you run it, you should see:

walking

 

Pretty cool!  However, instead of advancing the frame using code there is a much better approach to animation, that we will see in the next tutorial.  Stay tuned.

 

The Video

Programming


22. May 2015

 

Following the Beta release a few weeks back, the Godot Engine team have just released Godot Engine 1.1.

 

The highlights of the release are:

  • Rewritten Auto-Completion in the Code-Editor. Supports a lot of scenarios and perform smart-completion of node types if a scene where the script is being used is open.
  • Visual Shader Editor (Edit shaders connecting nodes)
  • New API in OS for managing the screens and window, with multi-monitor support.
  • Largely rewritten 2D engine, with support for:
    • Shaders (Visual and Code)
    • 2D Materials
    • 2D Independent Z ordering per-node.
    • 2D Lights
    • 2D Shadows with Polygonal Occluders
    • 2D Normal Mapping
    • Back-Buffer compositing for shaders that read from screen (allows all sorts of post-processing effects).
    • Improved Isometric TileMap Support (proper Z ordering of tiles and children nodes).
    • Distance-Field font support.
  • New 2D Navigation Polygon support, for efficient 2D pathfinding. Navigation Polygons can be edited visually and combined, disabled, etc.
  • Improved Usability in 2D Physics API:
    • Area2D and RigidBody2D can receive input events
    • Area2D can detect overlap with another Area2D
  • New Dark Theme
  • Much Improved Blender Collada Exporter (BetterCollada).
  • Large amount of bug fixes and smaller improvements.

 

Full (enormous) changelog since 1.0 here.

 

They also put together a video showcasing the new features:

 

Of course, if you are interested in learning more GFS has an extensive Godot Tutorial Series to get you started.

News


GFS On YouTube

See More Tutorials on DevGa.me!

Month List