updated diary entries

This commit is contained in:
chimchooree
2021-06-15 10:13:12 -05:00
parent 62d81d56ae
commit dba79f113c
5 changed files with 112 additions and 37 deletions
+78 -9
View File
@@ -1,19 +1,34 @@
<!--210429,210402-->
<h1>how my characters follow a moving target </h1>
<h1>follow a moving target </h1>
june 10, 2021<br>
#ai #character #movement <br>
<br>
After redesigning movement to support <a href="../210429">patrols</a>, I realized the path remains static even if the target moves. Time to tweak the design and debug. <br>
After redesigning the movement system to support <a href="../entries/210429">patrols</a>, I realized the path remains static even if the target moves. Time to tweak the design again. <br>
<br>
<h2>what must be done </h2><br>
Autopathing to attack targets, skill targets, and item targets still partially rely on an old version of the movement system. Also, characters never update their pathfinding, so they cannot pursue moving targets. With some changes, the movement system can officially support following any of these targets, no matter where they go. <br>
Autopathing to attack targets, skill targets, and item targets still partially relies on an old version of the movement system. Also, characters never update their pathfinding, so they cannot pursue moving targets. With some changes, the movement system can officially support following any of these targets, no matter where they go. <br>
<br>
For now, I'll update the movement system so that the character can autopath after a moving item then pick it up once within reach. Since autopathing to items works identically to the others, the fix for them will be the same, too. <br>
For now, I'll update the movement system so the character can autopath after a moving item then pick it up once within reach. Since autopathing to items works identically to the others, the fix will be the same, too. <br>
<br>
<h2>upgrading the movement system </h2><br>
I can keep the same system more or less, but one function is going to have to be rewritten: the character's path_to_object method. <br>
<br>
Before, it only set the next waypoint and built a path between the character and the waypoint. In order to follow moving targets, it needs to use the goal object itself. Also, it needs to be know when to only rebuild part of the path. <br>
Before, it only set the next waypoint and built a path between the character and the waypoint. <br>
<br>
<h3>old path_to_object </h3><br>
<code># Set Dots Between Character + Given Object<br>
func path_to_object(goal):<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if goal.is_in_group("waypoint"):<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set_next(goal)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else: <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;room.add_waypoint(goal.get_gpos())<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;path_to_position(goal.get_gpos())<br>
<br>
# Set Dots Between Character + Given Position<br>
func path_to_position(goal_pos):<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set_dots(find_dots_pos(goal_pos))</code><br>
<br>
In order to follow moving targets, it needs to use the goal object itself. Also, it needs to be know when to only rebuild part of the path. <br>
<br>
<h3>the character receives a waypoint instead of the target, so he is unaware of his target's movement </h3><br>
It took waypoints instead of the goal in the first place for consistency's sake. Since a target can either be a position (as with click-to-move) or an object (as with autopath-to-item) and the movement system only has one entry point, the old system only accepted objects. So when clicking-to-move, a Position2D waypoint is generated at the global mouse position. <br>
@@ -29,23 +44,77 @@ For two, it's bad for performance. Generally, the efficiency of a lightweight Go
<br>
The next playable release after the bingo version will have a teleporting boss, so I'll probably need to be more thoughtful about pathfinding then. For now, though, these two fixes should do it... <br>
<br>
<h3>new path_to_object </h3><br>
# Set Dots Between Character + Given Object<br>
func path_to_object(goal):<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Next Waypoint<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set_next(goal)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var goal_pos = goal.get_gpos()<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var dots = get_dots()<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var cd = get_current_dot()<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# If no current dot, set dots between user and goal<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if cd == null:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set_dots(find_dots_pos(get_gpos(), goal_pos))<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageBus.publish("moved", self)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var last_dot = cd if len(dots) == 0 else dots.back()<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Make sure goal has moved significantly<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if goal_pos.distance_to(last_dot) <= get_intimate_space():<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# If goal moved further away<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if get_gpos().distance_to(last_dot) > find_distance(goal):<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# If no dots, generate new ones<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if len(dots) == 0:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set_dots(find_dots_pos(get_gpos(), goal_pos))<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageBus.publish("moved", self)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# If dots, only recalculate part of the path<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var near = get_dots()[0]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for dot in get_dots():<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if dot.distance_to(goal_pos) < near.distance_to(goal_pos):<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;near = dot<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var i = get_dots().find(near)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set_dots(get_dots().slice(0, i - 1) +<br> find_dots_pos(get_dots()[i-1], goal_pos))<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# If goal moved closer<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set_dots(find_dots_pos(last_dot, goal_pos))<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageBus.publish("moved", self)</code><br>
<br>
<h2>testing </h2><br>
Now let's test it. I don't have any items that move around, so I'll quickly throw that in. I'll add some movement based on a sine wave into the _process method.<br>
<br>
<code>var time = 0
func _process(delta):
time += delta
var mod = Vector2(delta, cos(time) * 2)
set_gpos(get_gpos() + mod)</code><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;time += delta
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var mod = Vector2(delta, cos(time) * 2)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set_gpos(get_gpos() + mod)</code><br>
<br>
<br>
<h3>the character never stops autopathing, even after picking up the item </h3>
<br>
Okay, one more fix, then I'll have it. <br>
<br>
Previously, the movement AI relied on conditional statements in its process to detemine arrival at the goal. Instead, the <a href="../210402">achievement system</a> handles arrival for the new movement system. Since the process is called faster than the event handlers can function, the old AI system picked up and queue_free'd the floor item before the new system could recognize it had arrived at the goal. This meant the character never truly arrived and never knew to halt the movement process or clear movement variables. <br>
Previously, the movement AI relied on conditional statements in its process to detemine arrival at the goal. Instead, the <a href="../entries/210402">achievement system</a> handles arrival for the new movement system. Since the process is called faster than the event handlers can function, the old AI system picked up and queue_free'd the floor item before the new system could recognize it had arrived at the goal. This meant the character never truly arrived and never knew to halt the movement process or clear movement variables. <br>
<br>
Moving the conditional statements from _process to the function that handles the outcome of movement events. <br>
<br>
<h3>new result code </h3><br>
<code>#enum MOVED {ARRIVED, NOT_MOVING, TOO_SLOW, WRONG_DIRECTION}<br>
func handle_moved_result(result, new_user):<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if result == 0:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_user.think("UserMove/Arrived")<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if get_user().get_target() != null:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if track:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if get_user().find_distance_to_target() <= get_user().get_intimate_space():<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;emit_signal('item_arrived')<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get_waypoints().pop_front()<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_user.clear_movement()<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return</code><br>
<br>
<h2>just a few changes makes a big difference </h2><br>
<center>
<img src="/static/img/ent/follow.gif" alt="(image: Angel adjusts her pathfinding to follow a moving item.)"></center><br>
<br>
Last Updated June 12, 2021
<br>