6 min read

The Godot Engine has built-in functionalities that makes it easy to create navigation in the game world. This post will cover how to make an object follow a fixed path and how to go between two points avoiding the obstacles in the way.

Following a fixed path

Godot has a couple of nodes that help you create a path that can be followed by another node. One use of this is to make an NPC follow a fixed path in the map.

Assuming you have a new project, create a Path2D node. You can then use the controls on the toolbar to make a curve representing the path you will need to follow.

Curve buttons

After adding the points and adjusting the curves, you will have something like the following:

Path curve

Now you need to add a PathFollow2D node as a child of Path2D. This will do the actual movement based on the Offset property. Then add an AnimationPlayer node as child of the PathFollow2D.

Create a new animation in the player. Set the length to five seconds. Add a keyframe on the start with value 0 for the Unit Offset property of PathFollow2D. You can do that by clicking on the key icon next to the property in the Inspector dock. Then go to the end of the animation and add a frame with the value of 1. This will make Unit Offset go from 0 to 1 in the period of the animation (five seconds in this case). Set the animation to loop and autoplay.

To see the effect in practice, add a Sprite node as child of PathFollow2D. You can use the default Godot icon as the texture for it. Enable the Visible Navigation under the Debug Options menu (last button in the top center bar) to make it easier to see. Save the scene and play it to see the Godot robot run around the screen:

Sprite following path

That’s it! Making an object follow a fixed path is quite easy with the built-in resources of Godot. Not even scripting is needed for this example.

Navigation and Avoiding Obstacles

Sometimes you don’t have a fixed path to follow. It might change dynamically or your AI must determine the path and avoid the obstacles and walls that might be in the way. Don’t worry because Godot will also help you in this regard.

Create a new scene and add a Node2D as the root. Then add a Navigation2D as its child. This will be responsible for creating the paths for you. You now need to add a NavigationPolygonInstance node as child of the Navigation2D. This will hold the polygon used for navigation, to determine what the passable areas are.

To create the polygon itself, click on the pencil button on the toolbar (it will appear only if the NavigationPolygonInstance node is selected). The first time you try to add a point, the editor will warn you that there’s no NavigationPolygon resource and will offer you to create one. Click on the Create button and all will be set.

Navitation resource warning

First you need to create the outer boundaries of the navigable area. The polygon can have as many points as you need, but it does need to be a closed polygon. Note that you can right-click on points to remove them and hold Ctrl while clicking on lines to add points. Once you finish the boundaries, click the pencil button again and create polygons inside it to make the impassable areas, such as the walls. You will end up with something like the following:

Navigation polygon

Add a Sprite node as child of the root Node2D and set the texture of it (you can use the default Godot icon). This will be the object navigating through the space. Now add the following script to the root node. The most important detail here is the get_simple_path function, which returns a list of points to travel from start to end without passing through the walls.

extends Node2D

# Global variables
var start = Vector2()
var end = Vector2()
var path = []
var speed = 1
var transition = 0
var path_points = 0

func _ready():
    # Enable the processing of user input
    set_process_input(true)
    # Enable the general process callback
    set_process(true)

func _input(event):
    # If the user press a mouse button
    if event.type == InputEvent.MOUSE_BUTTON and event.pressed:
        if event.button_index == BUTTON_LEFT:
            # If it's the left button, set the starting point
            start = event.global_pos
        elif event.button_index == BUTTON_RIGHT:
            # If it's the right button, set the ending point
            end = event.global_pos
        # Reset the sprite position
        get_node("Sprite").set_global_pos(start)
        transition = 0

func _process(delta):
    # Get the list of points that compose the path
    path = get_node("Navigation2D").get_simple_path(start, end)
    # If the path has less points than it did before, reset the transition point
    if path.size() < path_points:
        transition = 0

    # Update the current amount of points
    path_points = path.size()

    # If there's less than 2 points, nothing can be done
    if path_points < 2: return

    var sprite = get_node("Sprite")
    # This uses the linear interpolation function from Vector2 to move the sprite in a constant
    # rate through the points of the path. Transition is a value from 0 to 1 to be used as a ratio.
    sprite.set_global_pos(sprite.get_global_pos().linear_interpolate(path[1], transition))
    start = sprite.get_global_pos()
    transition += speed * delta
    # Reset the transition when it gets to the point.
    if transition > 1: transition = 0

    # Update the node so the _draw() function is called
    update()

func _draw():
    # This draw a white circle with radius of 10px for each point in the path
    for p in path:
        draw_circle(p, 10, Color(1, 1, 1))

Enable the Visible Navigation in the Debug Options button to help you visualize the effect. Save and run the scene. You can then left-click somewhere to define a starting point, and right-click to define the ending point. The points will be marked as white circles and the Sprite will follow the path, clearing the intermediate points as it travels along.

Navigating Godot bot

Conclusion

The Godot Engine has many features to ease the development of all kinds of games. The navigation functions have many utilities in top-down games, be it an RPG or an RTS. Tilesets also embed navigation polygons that can be used in a similar fashion.

About the Author:

George Marques is a Brazilian software developer who has been playing with programming in a variety of environments since he was a kid. He works as a freelancer programmer for web technologies based on open source solutions such as WordPress and Open Journal Systems. He’s also one of the regular contributors of the Godot Engine, helping solving bugs and adding new features to the software, while also giving solutions to the community for the questions they have.

1 COMMENT

  1. 1.this line
    if path.size() 1: transition = 0
    is throwing this error:
    ‘:’ expected at end of line
    And this line:
    if event.type == InputEvent.MOUSE_BUTTON and event.pressed:
    Is giving this error:
    Invalid get index ‘type’ (on base: ‘InputEventMouseMotion’)

    Please help

LEAVE A REPLY

Please enter your comment!
Please enter your name here