Skip to content

Commit 16a1069

Browse files
committed
Add NavigationComponent prototype
1 parent b9460db commit 16a1069

File tree

5 files changed

+109
-2
lines changed

5 files changed

+109
-2
lines changed

Components/Movement/ChaseComponent.gd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## Moves the parent entity to chase after another [Node2D] by manipulating the entity's [member OverheadPhysicsComponent.inputDirection].
22
## Speed, acceleration and friction are determined by the [OverheadPhysicsComponent] and its [OverheadMovementParameters].
33
## NOTE: Set [member CharacterBodyComponent.shouldResetVelocityIfZeroMotion] to `false`
4+
## TIP: For more complex pathfinding based on Godot's Navigation nodes, use [NavigationComponent]
45
## Requirements: BEFORE [OverheadPhysicsComponent]
56

67
class_name ChaseComponent
@@ -16,6 +17,7 @@ extends CharacterBodyManipulatingComponentBase
1617
@export var shouldChasePlayerIfUnspecified: bool = true
1718

1819
@export var isEnabled: bool = true
20+
1921
#endregion
2022

2123

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
## A component which is a [NavigationAgent2D] node for pathfinding. Directs the parent entity towards another node specified as the destination,
2+
## while avoiding obstacles such as walls. Set the [member NavigationAgent2D.navigation_layers], [member NavigationAgent2D.avoidance_layers] and masks etc. as proper for your gameplay.
3+
## Uses an internal [Timer] $DestinationUpdateTimer to update the final target destination, instead of every frame, though the immediate movement direction is calculated each frame.
4+
## TIP: For a simple node-chasing component without pathfinding, use [ChaseComponent]
5+
## @experimental
6+
7+
class_name NavigationComponent
8+
extends Component
9+
10+
11+
#region Parameters
12+
13+
## If not specified and [member shouldChasePlayerIfUnspecified], then the first [PlayerEntity] from [member GameState.players] will be chosen.
14+
@export var destinationNode: Node2D:
15+
set(newValue):
16+
if newValue != destinationNode:
17+
destinationNode = newValue
18+
if destinationNode and self.is_node_ready():
19+
updateTargetPosition()
20+
$DestinationUpdateTimer.start()
21+
else:
22+
$DestinationUpdateTimer.stop()
23+
24+
## If `true` amd [member destinationNode] is `null`, the first [PlayerEntity] from [member GameState.players] will be chosen.
25+
@export var shouldChasePlayerIfUnspecified: bool = true
26+
27+
@export var isEnabled: bool = true:
28+
set(newValue):
29+
if newValue != isEnabled:
30+
isEnabled = newValue
31+
if isEnabled and destinationNode and self.is_node_ready(): $DestinationUpdateTimer.start()
32+
else: $DestinationUpdateTimer.stop()
33+
34+
#endregion
35+
36+
37+
#region State
38+
var selfAsAgent: NavigationAgent2D # Needed because [Component] extends [Node]
39+
var recentDirection: Vector2
40+
#endregion
41+
42+
43+
#region Dependencies
44+
#endregion
45+
46+
47+
func _ready() -> void:
48+
selfAsAgent = self.get_node(".") as NavigationAgent2D
49+
# if not characterBodyComponent.shouldResetVelocityIfZeroMotion:
50+
# printLog("characterBodyComponent.shouldResetVelocityIfZeroMotion = false")
51+
# characterBodyComponent.shouldResetVelocityIfZeroMotion = false
52+
53+
if not destinationNode and shouldChasePlayerIfUnspecified:
54+
destinationNode = GameState.players.front()
55+
56+
updateTargetPosition()
57+
$DestinationUpdateTimer.start()
58+
59+
60+
func updateTargetPosition() -> void:
61+
if isEnabled and destinationNode:
62+
selfAsAgent.target_position = destinationNode.global_position
63+
64+
65+
func onDestinationUpdateTimer_timeout() -> void:
66+
updateTargetPosition() # PERFORMANCE: Update periodically, not every frame!
67+
68+
69+
func _physics_process(_delta: float) -> void:
70+
if not isEnabled or not destinationNode: return
71+
self.recentDirection = parentEntity.to_local(selfAsAgent.get_next_path_position()).normalized()
72+
moveTowardsDestination()
73+
74+
if debugMode: showDebugInfo()
75+
76+
77+
## Moves the parent entity in the [member recentDirection] towards the [member NavigationAgent2D.target_position]
78+
## The default implementation sets the [member Node2D.position] directly, foregoing physics.
79+
## NOTE: Override this method in subclasses to implement different ways to move an entity, such as via [member CharacterBody2D.velocity] physics etc.
80+
func moveTowardsDestination() -> void:
81+
parentEntity.position += self.recentDirection
82+
83+
84+
func showDebugInfo() -> void:
85+
if not debugMode: return
86+
Debug.watchList[str("\n— ", parentEntity.name, ".", self.name)] = ""
87+
Debug.watchList.recentDirection = self.recentDirection
88+
Debug.watchList.destination = destinationNode.global_position
89+
Debug.watchList.target = selfAsAgent.target_position
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://bmhisv0p7fmwn
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[gd_scene load_steps=2 format=3 uid="uid://cilj7md5n141e"]
2+
3+
[ext_resource type="Script" uid="uid://bmhisv0p7fmwn" path="res://Components/Movement/NavigationComponent.gd" id="1_y74xm"]
4+
5+
[node name="NavigationComponent" type="NavigationAgent2D" groups=["components"]]
6+
avoidance_enabled = true
7+
radius = 16.0
8+
avoidance_mask = 16
9+
script = ExtResource("1_y74xm")
10+
11+
[node name="DestinationUpdateTimer" type="Timer" parent="."]
12+
process_callback = 0
13+
wait_time = 0.5
14+
ignore_time_scale = true
15+
16+
[connection signal="timeout" from="DestinationUpdateTimer" to="." method="onDestinationUpdateTimer_timeout"]

ToDo.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
## Major
44

5-
- [ ] Pathfinding/Navigation Components
6-
75
- [ ] Standard architecture for game environment state and "current run" state
86

97
- [ ] Save/Load ...oh my godot
@@ -19,6 +17,7 @@
1917

2018
## Done
2119

20+
- [?] Pathfinding/Navigation Components
2221
- [x] Player Settings
2322
- [x] Random populating Area2D
2423
- [x] Add `groups` parameter for node spawning functions

0 commit comments

Comments
 (0)