Gamedev Pills - Pieces of learning and gaming thoughts
Gamedev Pills - Pieces of learning and gaming thoughts
Game Dev Pill #1: NPCs & Environment (Part 2/2)
0:00
-0:57

Game Dev Pill #1: NPCs & Environment (Part 2/2)

Our NPC alive in just 6 minutes using TileMapLayer and NavigationRegion2D
Gamedev Pills
Game Dev Pill #1: NPCs & Environment (Part 1/2)
Welcome to the first Game Dev Pill class (Part 1/2) — focused on NPCs and environments…
Read more

Hello, fellow gamedevs! 👋

In our last session, we set the fundamentals for our NPC and environment. Today, we have a clear goal: make an NPC traverse our city like a real citizen — using TileMapLayer custom data and NavigationAgent2D.

Before we start, I recommend brushing up on some basic programming concepts. A great starting point is this interactive website: [link].

We also need:

  • Motivation

  • Godot 4.4.1

Thanks for reading Gamedev Pills! Subscribe for free to receive new posts and support my work.

Let’s create! 🎮

1. Add custom TileMapLayer data

We are going to create a variable included on our TileMapLayer called “walkable” with boolean type to really identify if the tile is walkable for our NPC or not. (Road vs sidewalk kind of)

2. Attach NPC script using GDScript

This is our code to have a smart NPC 😊(starting on second 0:30)

extends CharacterBody2D

var tilemap_city: TileMapLayer
var npc_target: Vector2i
var npc_in_map_position: Vector2i
var speed = 30

func _ready():
	tilemap_city = get_parent().get_node("NavigationRegion2D/TileMapLayer")
	npc_target = position
	npc_in_map_position = tilemap_city.local_to_map(position)
	set_walk_target()

func last_walkable_cell_in_direction(cell, direction) -> Vector2i:
	var cell_data = cell
	if (cell != tilemap_city.get_neighbor_cell(cell,direction)) && tilemap_city.get_cell_tile_data(tilemap_city.get_neighbor_cell(cell,direction))  && (tilemap_city.get_cell_tile_data(tilemap_city.get_neighbor_cell(cell,direction)).get_custom_data("walkable")):
			cell_data = last_walkable_cell_in_direction(tilemap_city.get_neighbor_cell(cell,direction), direction)
	return cell_data
	
func set_walk_target() -> void:
	var next_target = [ TileSet.CELL_NEIGHBOR_BOTTOM_SIDE, TileSet.CELL_NEIGHBOR_RIGHT_SIDE, TileSet.CELL_NEIGHBOR_TOP_SIDE, TileSet.CELL_NEIGHBOR_LEFT_SIDE ].pick_random()
	npc_target = last_walkable_cell_in_direction(npc_in_map_position, next_target)
	$NavigationAgent2D.target_position = tilemap_city.map_to_local(npc_target)
		
func walk():
	var direction = ($NavigationAgent2D.get_next_path_position() - global_position).normalized()
	velocity = direction * speed

func _physics_process(delta):
	npc_in_map_position = tilemap_city.local_to_map(position)
	if $NavigationAgent2D.is_target_reached():
		set_walk_target()
	walk()
	move_and_slide()

3. Make it move!

Success at Gamedev today 🚀

Our little city just got its first citizen walking the streets — and this is only the beginning.


I’d love to hear how you bring life to your NPCs, or what tricks you use to make them feel natural.

Share this post with a fellow dev, drop your thoughts in the comments, and subscribe to join our growing community of creators building games together.

Share Gamedev Pills

Thanks for reading!