Skip to content

Commit b0e1cc0

Browse files
committed
Update 2D navigation demos
1 parent 7af4d28 commit b0e1cc0

File tree

5 files changed

+109
-114
lines changed

5 files changed

+109
-114
lines changed

2d/navigation/navigation.gd

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,44 @@
11
extends Navigation2D
22

3-
export(float) var CHARACTER_SPEED = 400.0
3+
export(float) var character_speed = 400.0
44
var path = []
55

6+
func _process(delta):
7+
var walk_distance = character_speed * delta
8+
move_along_path(walk_distance)
9+
10+
611
# The 'click' event is a custom input action defined in
7-
# Project > Project Settings > Input Map tab
12+
# Project > Project Settings > Input Map tab.
813
func _input(event):
9-
if not event.is_action_pressed('click'):
14+
if not event.is_action_pressed("click"):
1015
return
1116
_update_navigation_path($Character.position, get_local_mouse_position())
1217

1318

14-
func _update_navigation_path(start_position, end_position):
15-
# get_simple_path is part of the Navigation2D class
16-
# it returns a PoolVector2Array of points that lead you from the
17-
# start_position to the end_position
18-
path = get_simple_path(start_position, end_position, true)
19-
# The first point is always the start_position
20-
# We don't need it in this example as it corresponds to the character's position
21-
path.remove(0)
22-
set_process(true)
23-
24-
25-
func _process(delta):
26-
var walk_distance = CHARACTER_SPEED * delta
27-
move_along_path(walk_distance)
28-
29-
3019
func move_along_path(distance):
3120
var last_point = $Character.position
3221
while path.size():
3322
var distance_between_points = last_point.distance_to(path[0])
34-
35-
# the position to move to falls between two points
23+
# The position to move to falls between two points.
3624
if distance <= distance_between_points:
3725
$Character.position = last_point.linear_interpolate(path[0], distance / distance_between_points)
3826
return
39-
40-
# the position is past the end of the segment
27+
# The position is past the end of the segment.
4128
distance -= distance_between_points
4229
last_point = path[0]
4330
path.remove(0)
44-
# the character reached the end of the path
31+
# The character reached the end of the path.
4532
$Character.position = last_point
4633
set_process(false)
34+
35+
36+
func _update_navigation_path(start_position, end_position):
37+
# get_simple_path is part of the Navigation2D class.
38+
# It returns a PoolVector2Array of points that lead you
39+
# from the start_position to the end_position.
40+
path = get_simple_path(start_position, end_position, true)
41+
# The first point is always the start_position.
42+
# We don't need it in this example as it corresponds to the character's position.
43+
path.remove(0)
44+
set_process(true)

2d/navigation_astar/Game.tscn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[ext_resource path="res://character.gd" type="Script" id=3]
66
[ext_resource path="res://sprites/character.png" type="Texture" id=4]
77

8-
[node name="Game" type="Node"]
8+
[node name="Game" type="Node2D"]
99

1010
[node name="TileMap" type="TileMap" parent="."]
1111
tile_set = ExtResource( 1 )

2d/navigation_astar/character.gd

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
extends Position2D
22

3-
export(float) var SPEED = 200.0
3+
enum States { IDLE, FOLLOW }
44

5-
enum STATES { IDLE, FOLLOW }
5+
export(float) var speed = 200.0
66
var _state = null
77

88
var path = []
@@ -12,49 +12,49 @@ var target_position = Vector2()
1212
var velocity = Vector2()
1313

1414
func _ready():
15-
_change_state(STATES.IDLE)
16-
17-
18-
func _change_state(new_state):
19-
if new_state == STATES.FOLLOW:
20-
path = get_parent().get_node('TileMap')._get_path(position, target_position)
21-
if not path or len(path) == 1:
22-
_change_state(STATES.IDLE)
23-
return
24-
# The index 0 is the starting cell
25-
# we don't want the character to move back to it in this example
26-
target_point_world = path[1]
27-
_state = new_state
15+
_change_state(States.IDLE)
2816

2917

3018
func _process(_delta):
31-
if not _state == STATES.FOLLOW:
19+
if not _state == States.FOLLOW:
3220
return
3321
var arrived_to_next_point = move_to(target_point_world)
3422
if arrived_to_next_point:
3523
path.remove(0)
3624
if len(path) == 0:
37-
_change_state(STATES.IDLE)
25+
_change_state(States.IDLE)
3826
return
3927
target_point_world = path[0]
4028

4129

30+
func _input(event):
31+
if event.is_action_pressed("click"):
32+
if Input.is_key_pressed(KEY_SHIFT):
33+
global_position = get_global_mouse_position()
34+
else:
35+
target_position = get_global_mouse_position()
36+
_change_state(States.FOLLOW)
37+
38+
4239
func move_to(world_position):
4340
var MASS = 10.0
4441
var ARRIVE_DISTANCE = 10.0
4542

46-
var desired_velocity = (world_position - position).normalized() * SPEED
43+
var desired_velocity = (world_position - position).normalized() * speed
4744
var steering = desired_velocity - velocity
4845
velocity += steering / MASS
4946
position += velocity * get_process_delta_time()
5047
rotation = velocity.angle()
5148
return position.distance_to(world_position) < ARRIVE_DISTANCE
5249

5350

54-
func _input(event):
55-
if event.is_action_pressed('click'):
56-
if Input.is_key_pressed(KEY_SHIFT):
57-
global_position = get_global_mouse_position()
58-
else:
59-
target_position = get_global_mouse_position()
60-
_change_state(STATES.FOLLOW)
51+
func _change_state(new_state):
52+
if new_state == States.FOLLOW:
53+
path = get_parent().get_node("TileMap")._get_path(position, target_position)
54+
if not path or len(path) == 1:
55+
_change_state(States.IDLE)
56+
return
57+
# The index 0 is the starting cell
58+
# we don't want the character to move back to it in this example
59+
target_point_world = path[1]
60+
_state = new_state

2d/navigation_astar/pathfind_astar.gd

Lines changed: 60 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
extends TileMap
22

3-
# You can only create an AStar node from code, not from the Scene tab
4-
onready var astar_node = AStar.new()
5-
# The Tilemap node doesn't have clear bounds so we're defining the map's limits here
3+
const BASE_LINE_WIDTH = 3.0
4+
const DRAW_COLOR = Color.white
5+
6+
# The Tilemap node doesn't have clear bounds so we're defining the map's limits here.
67
export(Vector2) var map_size = Vector2(16, 16)
78

8-
# The path start and end variables use setter methods
9-
# You can find them at the bottom of the script
9+
# The path start and end variables use setter methods.
10+
# You can find them at the bottom of the script.
1011
var path_start_position = Vector2() setget _set_path_start_position
1112
var path_end_position = Vector2() setget _set_path_end_position
1213

1314
var _point_path = []
1415

15-
const BASE_LINE_WIDTH = 3.0
16-
const DRAW_COLOR = Color('#fff')
17-
18-
# get_used_cells_by_id is a method from the TileMap node
19-
# here the id 0 corresponds to the grey tile, the obstacles
16+
# You can only create an AStar node from code, not from the Scene tab.
17+
onready var astar_node = AStar.new()
18+
# get_used_cells_by_id is a method from the TileMap node.
19+
# Here the id 0 corresponds to the grey tile, the obstacles.
2020
onready var obstacles = get_used_cells_by_id(0)
2121
onready var _half_cell_size = cell_size / 2
2222

@@ -25,8 +25,25 @@ func _ready():
2525
astar_connect_walkable_cells(walkable_cells_list)
2626

2727

28-
# Click and Shift force the start and end position of the path to update
29-
# and the node to redraw everything
28+
func _draw():
29+
if not _point_path:
30+
return
31+
var point_start = _point_path[0]
32+
var point_end = _point_path[len(_point_path) - 1]
33+
34+
set_cell(point_start.x, point_start.y, 1)
35+
set_cell(point_end.x, point_end.y, 2)
36+
37+
var last_point = map_to_world(Vector2(point_start.x, point_start.y)) + _half_cell_size
38+
for index in range(1, len(_point_path)):
39+
var current_point = map_to_world(Vector2(_point_path[index].x, _point_path[index].y)) + _half_cell_size
40+
draw_line(last_point, current_point, DRAW_COLOR, BASE_LINE_WIDTH, true)
41+
draw_circle(current_point, BASE_LINE_WIDTH * 2.0, DRAW_COLOR)
42+
last_point = current_point
43+
44+
45+
# Click and Shift force the start and end position of the path to update,
46+
# and the node to redraw everything.
3047
#func _input(event):
3148
# if event.is_action_pressed('click') and Input.is_key_pressed(KEY_SHIFT):
3249
# # To call the setter method from this script we have to use the explicit self.
@@ -36,81 +53,88 @@ func _ready():
3653

3754

3855
# Loops through all cells within the map's bounds and
39-
# adds all points to the astar_node, except the obstacles
40-
func astar_add_walkable_cells(obstacles = []):
56+
# adds all points to the astar_node, except the obstacles.
57+
func astar_add_walkable_cells(obstacle_list = []):
4158
var points_array = []
4259
for y in range(map_size.y):
4360
for x in range(map_size.x):
4461
var point = Vector2(x, y)
45-
if point in obstacles:
62+
if point in obstacle_list:
4663
continue
4764

4865
points_array.append(point)
49-
# The AStar class references points with indices
66+
# The AStar class references points with indices.
5067
# Using a function to calculate the index from a point's coordinates
51-
# ensures we always get the same index with the same input point
68+
# ensures we always get the same index with the same input point.
5269
var point_index = calculate_point_index(point)
5370
# AStar works for both 2d and 3d, so we have to convert the point
54-
# coordinates from and to Vector3s
71+
# coordinates from and to Vector3s.
5572
astar_node.add_point(point_index, Vector3(point.x, point.y, 0.0))
5673
return points_array
5774

5875

59-
# Once you added all points to the AStar node, you've got to connect them
76+
# Once you added all points to the AStar node, you've got to connect them.
6077
# The points don't have to be on a grid: you can use this class
61-
# to create walkable graphs however you'd like
78+
# to create walkable graphs however you'd like.
6279
# It's a little harder to code at first, but works for 2d, 3d,
6380
# orthogonal grids, hex grids, tower defense games...
6481
func astar_connect_walkable_cells(points_array):
6582
for point in points_array:
6683
var point_index = calculate_point_index(point)
6784
# For every cell in the map, we check the one to the top, right.
68-
# left and bottom of it. If it's in the map and not an obstalce,
69-
# We connect the current point with it
85+
# left and bottom of it. If it's in the map and not an obstalce.
86+
# We connect the current point with it.
7087
var points_relative = PoolVector2Array([
7188
Vector2(point.x + 1, point.y),
7289
Vector2(point.x - 1, point.y),
7390
Vector2(point.x, point.y + 1),
7491
Vector2(point.x, point.y - 1)])
7592
for point_relative in points_relative:
7693
var point_relative_index = calculate_point_index(point_relative)
77-
7894
if is_outside_map_bounds(point_relative):
7995
continue
8096
if not astar_node.has_point(point_relative_index):
8197
continue
8298
# Note the 3rd argument. It tells the astar_node that we want the
83-
# connection to be bilateral: from point A to B and B to A
84-
# If you set this value to false, it becomes a one-way path
85-
# As we loop through all points we can set it to false
99+
# connection to be bilateral: from point A to B and B to A.
100+
# If you set this value to false, it becomes a one-way path.
101+
# As we loop through all points we can set it to false.
86102
astar_node.connect_points(point_index, point_relative_index, false)
87103

88104

89-
# This is a variation of the method above
90-
# It connects cells horizontally, vertically AND diagonally
105+
# This is a variation of the method above.
106+
# It connects cells horizontally, vertically AND diagonally.
91107
func astar_connect_walkable_cells_diagonal(points_array):
92108
for point in points_array:
93109
var point_index = calculate_point_index(point)
94110
for local_y in range(3):
95111
for local_x in range(3):
96112
var point_relative = Vector2(point.x + local_x - 1, point.y + local_y - 1)
97113
var point_relative_index = calculate_point_index(point_relative)
98-
99114
if point_relative == point or is_outside_map_bounds(point_relative):
100115
continue
101116
if not astar_node.has_point(point_relative_index):
102117
continue
103118
astar_node.connect_points(point_index, point_relative_index, true)
104119

105120

106-
func is_outside_map_bounds(point):
107-
return point.x < 0 or point.y < 0 or point.x >= map_size.x or point.y >= map_size.y
108-
109-
110121
func calculate_point_index(point):
111122
return point.x + map_size.x * point.y
112123

113124

125+
func clear_previous_path_drawing():
126+
if not _point_path:
127+
return
128+
var point_start = _point_path[0]
129+
var point_end = _point_path[len(_point_path) - 1]
130+
set_cell(point_start.x, point_start.y, -1)
131+
set_cell(point_end.x, point_end.y, -1)
132+
133+
134+
func is_outside_map_bounds(point):
135+
return point.x < 0 or point.y < 0 or point.x >= map_size.x or point.y >= map_size.y
136+
137+
114138
func _get_path(world_start, world_end):
115139
self.path_start_position = world_to_map(world_start)
116140
self.path_end_position = world_to_map(world_end)
@@ -126,39 +150,13 @@ func _recalculate_path():
126150
clear_previous_path_drawing()
127151
var start_point_index = calculate_point_index(path_start_position)
128152
var end_point_index = calculate_point_index(path_end_position)
129-
# This method gives us an array of points. Note you need the start and end
130-
# points' indices as input
153+
# This method gives us an array of points. Note you need the start and
154+
# end points' indices as input.
131155
_point_path = astar_node.get_point_path(start_point_index, end_point_index)
132-
# Redraw the lines and circles from the start to the end point
156+
# Redraw the lines and circles from the start to the end point.
133157
update()
134158

135159

136-
func clear_previous_path_drawing():
137-
if not _point_path:
138-
return
139-
var point_start = _point_path[0]
140-
var point_end = _point_path[len(_point_path) - 1]
141-
set_cell(point_start.x, point_start.y, -1)
142-
set_cell(point_end.x, point_end.y, -1)
143-
144-
145-
func _draw():
146-
if not _point_path:
147-
return
148-
var point_start = _point_path[0]
149-
var point_end = _point_path[len(_point_path) - 1]
150-
151-
set_cell(point_start.x, point_start.y, 1)
152-
set_cell(point_end.x, point_end.y, 2)
153-
154-
var last_point = map_to_world(Vector2(point_start.x, point_start.y)) + _half_cell_size
155-
for index in range(1, len(_point_path)):
156-
var current_point = map_to_world(Vector2(_point_path[index].x, _point_path[index].y)) + _half_cell_size
157-
draw_line(last_point, current_point, DRAW_COLOR, BASE_LINE_WIDTH, true)
158-
draw_circle(current_point, BASE_LINE_WIDTH * 2.0, DRAW_COLOR)
159-
last_point = current_point
160-
161-
162160
# Setters for the start and end path values.
163161
func _set_path_start_position(value):
164162
if value in obstacles:

2d/navigation_astar/tileset/tileset_source.tscn

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[ext_resource path="res://sprites/path_start.png" type="Texture" id=2]
55
[ext_resource path="res://sprites/path_end.png" type="Texture" id=3]
66

7-
[node name="Node2D" type="Node2D"]
7+
[node name="Tileset" type="Node2D"]
88

99
[node name="Obstacle" type="Sprite" parent="."]
1010
position = Vector2( 32, 32 )
@@ -17,4 +17,3 @@ texture = ExtResource( 2 )
1717
[node name="PathEnd" type="Sprite" parent="."]
1818
position = Vector2( 192, 32 )
1919
texture = ExtResource( 3 )
20-

0 commit comments

Comments
 (0)