Issues with combining A* with raycasting for smooth pathfinding and snapping to obstacle corners

I’ve updated the test map a little bit. Used only plain and void tiles now for better contrast. And added a step debugger with better visualisation. I hope this makes it more clear.

I will go more in depth to your reply, as I’m nearly falling asleep, but I did want to share some data in case anyone was willing to take a look.

Example 1

─── Step 1 / 29 ─────────────────────────
[cw #1] CORNER @ tile(6.2, 13.2) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=38 (bottom edge)
─── Step 2 / 29 ─────────────────────────
[cw #2] ALIGNED @ tile(4.8, 13.2) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=37 (bottom edge)
─── Step 3 / 29 ─────────────────────────
[cw #3] ALIGNED @ tile(3.8, 13.2) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=37 (bottom edge)
─── Step 4 / 29 ─────────────────────────
[cw #4] ALIGNED @ tile(2.8, 13.2) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=45 (top edge)
─── Step 5 / 29 ─────────────────────────
[cw #5] ALIGNED @ tile(1.8, 13.2) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=44 (right edge)
─── Step 6 / 29 ─────────────────────────
[cw #6] INTERSECTION @ tile(1.2, 13.2) | R=1 L=1 | ray: BLOCKED ✗ → hit rect=45 (left edge)
─── Step 7 / 29 ─────────────────────────
[cw #7] ALIGNED @ tile(1.2, 14.2) | R=1 L=1 | ray: BLOCKED ✗ → hit rect=44 (bottom edge)
─── Step 8 / 29 ─────────────────────────
[cw #8] INTERSECTION @ tile(1.2, 14.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=44 (bottom edge)
─── Step 9 / 29 ─────────────────────────
[cw #9] ALIGNED @ tile(2.2, 14.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=45 (bottom edge)
─── Step 10 / 29 ─────────────────────────
[cw #10] ALIGNED @ tile(3.2, 14.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=46 (bottom edge)
─── Step 11 / 29 ─────────────────────────
[cw #11] ALIGNED @ tile(4.2, 14.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=46 (bottom edge)
─── Step 12 / 29 ─────────────────────────
[cw #12] ALIGNED @ tile(5.2, 14.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=47 (bottom edge)
─── Step 13 / 29 ─────────────────────────
[cw #13] ALIGNED @ tile(6.2, 14.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=48 (bottom edge)
─── Step 14 / 29 ─────────────────────────
[cw #14] ALIGNED @ tile(7.2, 14.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=48 (right edge)
─── Step 15 / 29 ─────────────────────────
[cw #15] ALIGNED @ tile(8.2, 14.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=38 (right edge)
─── Step 16 / 29 ─────────────────────────
[cw #16] ALIGNED @ tile(9.2, 14.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=38 (right edge)
─── Step 17 / 29 ─────────────────────────
[cw #17] ALIGNED @ tile(10.2, 14.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=38 (right edge)
─── Step 18 / 29 ─────────────────────────
[cw #18] ALIGNED @ tile(11.2, 14.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=38 (right edge)
─── Step 19 / 29 ─────────────────────────
[cw #19] JUMPED to new obstacle group rect=26 edge=2 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=26 edge=2 — CHECK if this is a boundary rect!
─── Step 20 / 29 ─────────────────────────
[cw #20] CORNER @ tile(4.8, 10.2) | R=1 L=0 | ray: CLEAR ✓
─── Step 21 / 29 ─────────────────────────
[ccw #21] ALIGNED @ tile(6.2, 11.8) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=38 (top edge)
─── Step 22 / 29 ─────────────────────────
[ccw #22] JUMPED to new obstacle group rect=26 edge=2 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=26 edge=2 — CHECK if this is a boundary rect!
─── Step 23 / 29 ─────────────────────────
[ccw #23] ALIGNED @ tile(6.2, 10.2) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=27 (left edge)
─── Step 24 / 29 ─────────────────────────
[ccw #24] CORNER @ tile(7.2, 10.2) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=26 (right edge)
─── Step 25 / 29 ─────────────────────────
[ccw #25] ALIGNED @ tile(7.2, 8.8) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=22 (right edge)
─── Step 26 / 29 ─────────────────────────
[ccw #26] CORNER @ tile(7.2, 7.8) | R=0 L=2 | ray: BLOCKED ✗ → hit rect=22 (right edge)
─── Step 27 / 29 ─────────────────────────
[ccw #27] ALIGNED @ tile(5.8, 7.8) | R=0 L=2 | ray: BLOCKED ✗ → hit rect=22 (left edge)
─── Step 28 / 29 ─────────────────────────
[ccw #28] CORNER @ tile(4.8, 7.8) | R=0 L=3 | ray: CLEAR ✓
Example 2

─── Step 1 / 82 ─────────────────────────
[cw #1] CORNER @ tile(4.8, 7.8) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=34 (left edge)
─── Step 2 / 82 ─────────────────────────
[cw #2] ALIGNED @ tile(6.2, 7.8) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=34 (right edge)
─── Step 3 / 82 ─────────────────────────
[cw #3] JUMPED to new obstacle group rect=27 edge=3 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=27 edge=3 — CHECK if this is a boundary rect!
─── Step 4 / 82 ─────────────────────────
[cw #4] CORNER @ tile(7.8, 6.8) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=28 (left edge)
─── Step 5 / 82 ─────────────────────────
[cw #5] INTERSECTION @ tile(8.8, 6.8) | R=1 L=1 | ray: BLOCKED ✗ → hit rect=22 (bottom edge)
─── Step 6 / 82 ─────────────────────────
[cw #6] ALIGNED @ tile(8.8, 5.8) | R=1 L=1 | ray: BLOCKED ✗ → hit rect=16 (bottom edge)
─── Step 7 / 82 ─────────────────────────
[cw #7] ALIGNED @ tile(8.8, 4.8) | R=1 L=1 | ray: BLOCKED ✗ → hit rect=10 (bottom edge)
─── Step 8 / 82 ─────────────────────────
[cw #8] INTERSECTION @ tile(8.8, 3.9) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=3 (bottom edge)
─── Step 9 / 82 ─────────────────────────
[cw #9] INTERSECTION @ tile(7.2, 3.9) | R=1 L=3 | ray: BLOCKED ✗ → hit rect=22 (left edge)
─── Step 10 / 82 ─────────────────────────
[cw #10] ALIGNED @ tile(7.2, 4.2) | R=1 L=3 | ray: BLOCKED ✗ → hit rect=22 (left edge)
─── Step 11 / 82 ─────────────────────────
[cw #11] CORNER @ tile(7.2, 5.2) | R=2 L=3 | ray: BLOCKED ✗ → hit rect=27 (top edge)
─── Step 12 / 82 ─────────────────────────
[cw #12] ALIGNED @ tile(5.8, 5.2) | R=2 L=3 | ray: BLOCKED ✗ → hit rect=27 (left edge)
─── Step 13 / 82 ─────────────────────────
[cw #13] INTERSECTION @ tile(5.2, 5.2) | R=2 L=4 | ray: BLOCKED ✗ → hit rect=27 (left edge)
─── Step 14 / 82 ─────────────────────────
[cw #14] CORNER @ tile(5.2, 6.2) | R=3 L=4 | ray: BLOCKED ✗ → hit rect=27 (left edge)
─── Step 15 / 82 ─────────────────────────
[cw #15] ALIGNED @ tile(3.8, 6.2) | R=3 L=4 | ray: BLOCKED ✗ → hit rect=34 (top edge)
─── Step 16 / 82 ─────────────────────────
[cw #16] CORNER @ tile(2.8, 6.2) | R=4 L=4 | ray: BLOCKED ✗ → hit rect=34 (top edge)
─── Step 17 / 82 ─────────────────────────
[cw #17] ALIGNED @ tile(2.8, 4.8) | R=4 L=4 | ray: BLOCKED ✗ → hit rect=6 (bottom edge)
─── Step 18 / 82 ─────────────────────────
[cw #18] INTERSECTION @ tile(2.8, 3.9) | R=4 L=5 | ray: BLOCKED ✗ → hit rect=7 (left edge)
─── Step 19 / 82 ─────────────────────────
[cw #19] INTERSECTION @ tile(1.2, 3.9) | R=4 L=6 | ray: BLOCKED ✗ → hit rect=6 (left edge)
─── Step 20 / 82 ─────────────────────────
[cw #20] ALIGNED @ tile(1.2, 4.2) | R=4 L=6 | ray: BLOCKED ✗ → hit rect=6 (left edge)
─── Step 21 / 82 ─────────────────────────
[cw #21] ALIGNED @ tile(1.2, 5.2) | R=4 L=6 | ray: BLOCKED ✗ → hit rect=14 (left edge)
─── Step 22 / 82 ─────────────────────────
[cw #22] ALIGNED @ tile(1.2, 6.2) | R=4 L=6 | ray: BLOCKED ✗ → hit rect=33 (top edge)
─── Step 23 / 82 ─────────────────────────
[cw #23] ALIGNED @ tile(1.2, 7.2) | R=4 L=6 | ray: BLOCKED ✗ → hit rect=33 (left edge)
─── Step 24 / 82 ─────────────────────────
[cw #24] ALIGNED @ tile(1.2, 8.2) | R=4 L=6 | ray: BLOCKED ✗ → hit rect=33 (left edge)
─── Step 25 / 82 ─────────────────────────
[cw #25] ALIGNED @ tile(1.2, 9.2) | R=4 L=6 | ray: BLOCKED ✗ → hit rect=33 (left edge)
─── Step 26 / 82 ─────────────────────────
[cw #26] ALIGNED @ tile(1.2, 10.2) | R=4 L=6 | ray: BLOCKED ✗ → hit rect=38 (left edge)
─── Step 27 / 82 ─────────────────────────
[cw #27] ALIGNED @ tile(1.2, 11.2) | R=4 L=6 | ray: BLOCKED ✗ → hit rect=38 (bottom edge)
─── Step 28 / 82 ─────────────────────────
[cw #28] JUMPED to new obstacle group rect=53 edge=3 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=53 edge=3 — CHECK if this is a boundary rect!
─── Step 29 / 82 ─────────────────────────
[cw #29] CORNER @ tile(3.8, 10.8) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=38 (bottom edge)
─── Step 30 / 82 ─────────────────────────
[cw #30] ALIGNED @ tile(5.2, 10.8) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=39 (bottom edge)
─── Step 31 / 82 ─────────────────────────
[cw #31] JUMPED to new obstacle group rect=40 edge=3 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=40 edge=3 — CHECK if this is a boundary rect!
─── Step 32 / 82 ─────────────────────────
[cw #32] ALIGNED @ tile(8.8, 8.8) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=35 (right edge)
─── Step 33 / 82 ─────────────────────────
[cw #33] INTERSECTION @ tile(8.8, 8.2) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=40 (top edge)
─── Step 34 / 82 ─────────────────────────
[cw #34] CORNER @ tile(7.8, 8.2) | R=1 L=1 | ray: BLOCKED ✗ → hit rect=35 (left edge)
─── Step 35 / 82 ─────────────────────────
[cw #35] CORNER @ tile(7.8, 6.8) | R=2 L=1 | ray: BLOCKED ✗ → hit rect=28 (left edge)
─── Step 36 / 82 ─────────────────────────
[cw #36] INTERSECTION @ tile(8.8, 6.8) | R=2 L=2 | ray: BLOCKED ✗ → hit rect=22 (bottom edge)
─── Step 37 / 82 ─────────────────────────
[cw #37] ALIGNED @ tile(8.8, 5.8) | R=2 L=2 | ray: BLOCKED ✗ → hit rect=16 (bottom edge)
─── Step 38 / 82 ─────────────────────────
[cw #38] ALIGNED @ tile(8.8, 4.8) | R=2 L=2 | ray: BLOCKED ✗ → hit rect=10 (bottom edge)
─── Step 39 / 82 ─────────────────────────
[cw #39] INTERSECTION @ tile(8.8, 3.9) | R=2 L=3 | ray: BLOCKED ✗ → hit rect=3 (bottom edge)
─── Step 40 / 82 ─────────────────────────
[cw #40] INTERSECTION @ tile(7.2, 3.9) | R=2 L=4 | ray: BLOCKED ✗ → hit rect=22 (left edge)
─── Step 41 / 82 ─────────────────────────
[cw #41] ALIGNED @ tile(7.2, 4.2) | R=2 L=4 | ray: BLOCKED ✗ → hit rect=22 (left edge)
─── Step 42 / 82 ─────────────────────────
[cw #42] CORNER @ tile(7.2, 5.2) | R=3 L=4 | ray: BLOCKED ✗ → hit rect=27 (top edge)
─── Step 43 / 82 ─────────────────────────
[cw #43] ALIGNED @ tile(5.8, 5.2) | R=3 L=4 | ray: BLOCKED ✗ → hit rect=27 (left edge)
─── Step 44 / 82 ─────────────────────────
[cw #44] INTERSECTION @ tile(5.2, 5.2) | R=3 L=5 | ray: BLOCKED ✗ → hit rect=27 (left edge)
─── Step 45 / 82 ─────────────────────────
[cw #45] CORNER @ tile(5.2, 6.2) | R=4 L=5 | ray: BLOCKED ✗ → hit rect=27 (left edge)
─── Step 46 / 82 ─────────────────────────
[cw #46] ALIGNED @ tile(3.8, 6.2) | R=4 L=5 | ray: BLOCKED ✗ → hit rect=34 (top edge)
─── Step 47 / 82 ─────────────────────────
[cw #47] CORNER @ tile(2.8, 6.2) | R=5 L=5 | ray: BLOCKED ✗ → hit rect=34 (top edge)
─── Step 48 / 82 ─────────────────────────
[cw #48] ALIGNED @ tile(2.8, 4.8) | R=5 L=5 | ray: BLOCKED ✗ → hit rect=6 (bottom edge)
─── Step 49 / 82 ─────────────────────────
[cw #49] INTERSECTION @ tile(2.8, 3.9) | R=5 L=6 | ray: BLOCKED ✗ → hit rect=7 (left edge)
─── Step 50 / 82 ─────────────────────────
[cw #50] INTERSECTION @ tile(1.2, 3.9) | R=5 L=7 | ray: BLOCKED ✗ → hit rect=6 (left edge)
─── Step 51 / 82 ─────────────────────────
[cw #51] ALIGNED @ tile(1.2, 4.2) | R=5 L=7 | ray: BLOCKED ✗ → hit rect=6 (left edge)
─── Step 52 / 82 ─────────────────────────
[cw #52] ALIGNED @ tile(1.2, 5.2) | R=5 L=7 | ray: BLOCKED ✗ → hit rect=14 (left edge)
─── Step 53 / 82 ─────────────────────────
[cw #53] ALIGNED @ tile(1.2, 6.2) | R=5 L=7 | ray: BLOCKED ✗ → hit rect=33 (top edge)
─── Step 54 / 82 ─────────────────────────
[cw #54] ALIGNED @ tile(1.2, 7.2) | R=5 L=7 | ray: BLOCKED ✗ → hit rect=33 (left edge)
─── Step 55 / 82 ─────────────────────────
[cw #55] ALIGNED @ tile(1.2, 8.2) | R=5 L=7 | ray: BLOCKED ✗ → hit rect=33 (left edge)
─── Step 56 / 82 ─────────────────────────
[cw #56] ALIGNED @ tile(1.2, 9.2) | R=5 L=7 | ray: BLOCKED ✗ → hit rect=33 (left edge)
─── Step 57 / 82 ─────────────────────────
[cw #57] ALIGNED @ tile(1.2, 10.2) | R=5 L=7 | ray: BLOCKED ✗ → hit rect=38 (left edge)
─── Step 58 / 82 ─────────────────────────
[cw #58] ALIGNED @ tile(1.2, 11.2) | R=5 L=7 | ray: BLOCKED ✗ → hit rect=38 (bottom edge)
─── Step 59 / 82 ─────────────────────────
[cw #59] INTERSECTION @ tile(1.2, 11.8) | R=5 L=8 | ray: BLOCKED ✗ → hit rect=53 (left edge)
─── Step 60 / 82 ─────────────────────────
[cw #60] ALIGNED @ tile(2.2, 11.8) | R=5 L=8 | ray: BLOCKED ✗ → hit rect=53 (left edge)
─── Step 61 / 82 ─────────────────────────
[cw #61] ALIGNED @ tile(3.2, 11.8) | R=5 L=8 | ray: BLOCKED ✗ → hit rect=53 (left edge)
─── Step 62 / 82 ─────────────────────────
[cw #62] INTERSECTION @ tile(3.8, 11.8) | R=5 L=9 | ray: BLOCKED ✗ → hit rect=54 (left edge)
─── Step 63 / 82 ─────────────────────────
[cw #63] HOLE DETECTED — goal is enclosed (L-R=4)
  ⚠ Walk terminated: hole_ended
─── Step 64 / 82 ─────────────────────────
[ccw #64] ALIGNED @ tile(4.8, 9.2) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=34 (left edge)
─── Step 65 / 82 ─────────────────────────
[ccw #65] CORNER @ tile(4.8, 10.2) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=39 (left edge)
─── Step 66 / 82 ─────────────────────────
[ccw #66] ALIGNED @ tile(6.2, 10.2) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=39 (right edge)
─── Step 67 / 82 ─────────────────────────
[ccw #67] JUMPED to new obstacle group rect=40 edge=3 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=40 edge=3 — CHECK if this is a boundary rect!
─── Step 68 / 82 ─────────────────────────
[ccw #68] ALIGNED @ tile(8.8, 10.2) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=46 (top edge)
─── Step 69 / 82 ─────────────────────────
[ccw #69] ALIGNED @ tile(8.8, 11.2) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=55 (top edge)
─── Step 70 / 82 ─────────────────────────
[ccw #70] CORNER @ tile(8.8, 12.2) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=46 (bottom edge)
─── Step 71 / 82 ─────────────────────────
[ccw #71] ALIGNED @ tile(10.2, 12.2) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=47 (bottom edge)
─── Step 72 / 82 ─────────────────────────
[ccw #72] ALIGNED @ tile(11.2, 12.2) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=47 (bottom edge)
─── Step 73 / 82 ─────────────────────────
[ccw #73] ALIGNED @ tile(12.2, 12.2) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=58 (left edge)
─── Step 74 / 82 ─────────────────────────
[ccw #74] ALIGNED @ tile(13.2, 12.2) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=59 (left edge)
─── Step 75 / 82 ─────────────────────────
[ccw #75] CORNER @ tile(14.2, 12.2) | R=0 L=2 | ray: BLOCKED ✗ → hit rect=58 (right edge)
─── Step 76 / 82 ─────────────────────────
[ccw #76] ALIGNED @ tile(14.2, 10.8) | R=0 L=2 | ray: BLOCKED ✗ → hit rect=49 (right edge)
─── Step 77 / 82 ─────────────────────────
[ccw #77] ALIGNED @ tile(14.2, 9.8) | R=0 L=2 | ray: BLOCKED ✗ → hit rect=42 (right edge)
─── Step 78 / 82 ─────────────────────────
[ccw #78] CORNER @ tile(14.2, 8.8) | R=0 L=3 | ray: BLOCKED ✗ → hit rect=42 (right edge)
─── Step 79 / 82 ─────────────────────────
[ccw #79] ALIGNED @ tile(12.8, 8.8) | R=0 L=3 | ray: BLOCKED ✗ → hit rect=41 (right edge)
─── Step 80 / 82 ─────────────────────────
[ccw #80] ALIGNED @ tile(11.8, 8.8) | R=0 L=3 | ray: BLOCKED ✗ → hit rect=41 (left edge)
─── Step 81 / 82 ─────────────────────────
[ccw #81] CORNER @ tile(10.8, 8.8) | R=0 L=4 | ray: CLEAR ✓

Looking at that second example, perhaps one optimisation is trying CW and CCW after jumping to another obstacle before disregarding it?

'Example 3

─── Step 1 / 110 ─────────────────────────
[cw #1] ALIGNED @ tile(10.2, 9.2) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=63 (top edge)
─── Step 2 / 110 ─────────────────────────
[cw #2] INTERSECTION @ tile(10.2, 9.8) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=58 (bottom edge)
─── Step 3 / 110 ─────────────────────────
[cw #3] INTERSECTION @ tile(10.8, 9.8) | R=0 L=2 | ray: BLOCKED ✗ → hit rect=63 (right edge)
─── Step 4 / 110 ─────────────────────────
[cw #4] CORNER @ tile(10.8, 8.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=58 (right edge)
─── Step 5 / 110 ─────────────────────────
[cw #5] ALIGNED @ tile(12.2, 8.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=51 (right edge)
─── Step 6 / 110 ─────────────────────────
[cw #6] JUMPED to new obstacle group rect=45 edge=1 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=45 edge=1 — CHECK if this is a boundary rect!
─── Step 7 / 110 ─────────────────────────
[cw #7] ALIGNED @ tile(10.2, 7.2) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=51 (top edge)
─── Step 8 / 110 ─────────────────────────
[cw #8] ALIGNED @ tile(10.2, 8.2) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=58 (top edge)
─── Step 9 / 110 ─────────────────────────
[cw #9] ALIGNED @ tile(10.2, 9.2) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=63 (top edge)
─── Step 10 / 110 ─────────────────────────
[cw #10] INTERSECTION @ tile(10.2, 9.8) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=58 (bottom edge)
─── Step 11 / 110 ─────────────────────────
[cw #11] INTERSECTION @ tile(10.8, 9.8) | R=0 L=2 | ray: BLOCKED ✗ → hit rect=63 (right edge)
─── Step 12 / 110 ─────────────────────────
[cw #12] CORNER @ tile(10.8, 8.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=58 (right edge)
─── Step 13 / 110 ─────────────────────────
[cw #13] ALIGNED @ tile(12.2, 8.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=51 (right edge)
─── Step 14 / 110 ─────────────────────────
[cw #14] ALIGNED @ tile(13.2, 8.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=45 (right edge)
─── Step 15 / 110 ─────────────────────────
[cw #15] JUMPED to new obstacle group rect=52 edge=2 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=52 edge=2 — CHECK if this is a boundary rect!
─── Step 16 / 110 ─────────────────────────
[cw #16] CORNER @ tile(12.8, 8.2) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=45 (right edge)
─── Step 17 / 110 ─────────────────────────
[cw #17] ALIGNED @ tile(12.8, 6.8) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=40 (right edge)
─── Step 18 / 110 ─────────────────────────
[cw #18] ALIGNED @ tile(12.8, 5.8) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=40 (right edge)
─── Step 19 / 110 ─────────────────────────
[cw #19] JUMPED to new obstacle group rect=34 edge=1 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=34 edge=1 — CHECK if this is a boundary rect!
─── Step 20 / 110 ─────────────────────────
[cw #20] ALIGNED @ tile(11.2, 5.2) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=33 (right edge)
─── Step 21 / 110 ─────────────────────────
[cw #21] CORNER @ tile(11.2, 6.2) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=39 (right edge)
─── Step 22 / 110 ─────────────────────────
[cw #22] INTERSECTION @ tile(10.2, 6.2) | R=1 L=1 | ray: BLOCKED ✗ → hit rect=40 (left edge)
─── Step 23 / 110 ─────────────────────────
[cw #23] ALIGNED @ tile(10.2, 7.2) | R=1 L=1 | ray: BLOCKED ✗ → hit rect=51 (top edge)
─── Step 24 / 110 ─────────────────────────
[cw #24] ALIGNED @ tile(10.2, 8.2) | R=1 L=1 | ray: BLOCKED ✗ → hit rect=58 (top edge)
─── Step 25 / 110 ─────────────────────────
[cw #25] ALIGNED @ tile(10.2, 9.2) | R=1 L=1 | ray: BLOCKED ✗ → hit rect=63 (top edge)
─── Step 26 / 110 ─────────────────────────
[cw #26] INTERSECTION @ tile(10.2, 9.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=58 (bottom edge)
─── Step 27 / 110 ─────────────────────────
[cw #27] INTERSECTION @ tile(10.8, 9.8) | R=1 L=3 | ray: BLOCKED ✗ → hit rect=63 (right edge)
─── Step 28 / 110 ─────────────────────────
[cw #28] CORNER @ tile(10.8, 8.8) | R=2 L=3 | ray: BLOCKED ✗ → hit rect=58 (right edge)
─── Step 29 / 110 ─────────────────────────
[cw #29] ALIGNED @ tile(12.2, 8.8) | R=2 L=3 | ray: BLOCKED ✗ → hit rect=51 (right edge)
─── Step 30 / 110 ─────────────────────────
[cw #30] ALIGNED @ tile(13.2, 8.8) | R=2 L=3 | ray: BLOCKED ✗ → hit rect=45 (right edge)
─── Step 31 / 110 ─────────────────────────
[cw #31] CORNER @ tile(14.2, 8.8) | R=3 L=3 | ray: BLOCKED ✗ → hit rect=52 (bottom edge)
─── Step 32 / 110 ─────────────────────────
[cw #32] ALIGNED @ tile(14.2, 10.2) | R=3 L=3 | ray: BLOCKED ✗ → hit rect=73 (top edge)
─── Step 33 / 110 ─────────────────────────
[cw #33] ALIGNED @ tile(14.2, 11.2) | R=3 L=3 | ray: BLOCKED ✗ → hit rect=82 (top edge)
─── Step 34 / 110 ─────────────────────────
[cw #34] CORNER @ tile(14.2, 12.2) | R=4 L=3 | ray: BLOCKED ✗ → hit rect=81 (right edge)
─── Step 35 / 110 ─────────────────────────
[cw #35] ALIGNED @ tile(12.8, 12.2) | R=4 L=3 | ray: BLOCKED ✗ → hit rect=80 (right edge)
─── Step 36 / 110 ─────────────────────────
[cw #36] ALIGNED @ tile(11.8, 12.2) | R=4 L=3 | ray: BLOCKED ✗ → hit rect=79 (right edge)
─── Step 37 / 110 ─────────────────────────
[cw #37] ALIGNED @ tile(10.8, 12.2) | R=4 L=3 | ray: BLOCKED ✗ → hit rect=78 (right edge)
─── Step 38 / 110 ─────────────────────────
[cw #38] ALIGNED @ tile(9.8, 12.2) | R=4 L=3 | ray: BLOCKED ✗ → hit rect=69 (bottom edge)
─── Step 39 / 110 ─────────────────────────
[cw #39] JUMPED to new obstacle group rect=62 edge=1 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=62 edge=1 — CHECK if this is a boundary rect!
─── Step 40 / 110 ─────────────────────────
[cw #40] CORNER @ tile(7.2, 10.2) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=57 (bottom edge)
─── Step 41 / 110 ─────────────────────────
[cw #41] ALIGNED @ tile(5.8, 10.2) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=56 (bottom edge)
─── Step 42 / 110 ─────────────────────────
[cw #42] JUMPED to new obstacle group rect=37 edge=2 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=37 edge=2 — CHECK if this is a boundary rect!
─── Step 43 / 110 ─────────────────────────
[cw #43] CORNER @ tile(2.8, 6.2) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=29 (bottom edge)
─── Step 44 / 110 ─────────────────────────
[cw #44] ALIGNED @ tile(2.8, 4.8) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=29 (top edge)
─── Step 45 / 110 ─────────────────────────
[cw #45] CORNER @ tile(2.8, 3.8) | R=2 L=0 | ray: CLEAR ✓
─── Step 46 / 110 ─────────────────────────
[ccw #46] ALIGNED @ tile(10.2, 7.8) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=45 (bottom edge)
─── Step 47 / 110 ─────────────────────────
[ccw #47] ALIGNED @ tile(10.2, 6.8) | R=0 L=0 | ray: BLOCKED ✗ → hit rect=39 (bottom edge)
─── Step 48 / 110 ─────────────────────────
[ccw #48] INTERSECTION @ tile(10.2, 6.2) | R=1 L=0 | ray: BLOCKED ✗ → hit rect=40 (left edge)
─── Step 49 / 110 ─────────────────────────
[ccw #49] CORNER @ tile(11.2, 6.2) | R=1 L=1 | ray: BLOCKED ✗ → hit rect=39 (right edge)
─── Step 50 / 110 ─────────────────────────
[ccw #50] ALIGNED @ tile(11.2, 4.8) | R=1 L=1 | ray: BLOCKED ✗ → hit rect=33 (right edge)
─── Step 51 / 110 ─────────────────────────
[ccw #51] CORNER @ tile(11.2, 3.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=26 (right edge)
─── Step 52 / 110 ─────────────────────────
[ccw #52] INTERSECTION @ tile(10.2, 3.8) | R=2 L=2 | ray: BLOCKED ✗ → hit rect=26 (left edge)
─── Step 53 / 110 ─────────────────────────
[ccw #53] ALIGNED @ tile(10.2, 2.8) | R=2 L=2 | ray: BLOCKED ✗ → hit rect=21 (left edge)
─── Step 54 / 110 ─────────────────────────
[ccw #54] ALIGNED @ tile(10.2, 1.8) | R=2 L=2 | ray: BLOCKED ✗ → hit rect=17 (left edge)
─── Step 55 / 110 ─────────────────────────
[ccw #55] INTERSECTION @ tile(10.2, 1.2) | R=3 L=2 | ray: BLOCKED ✗ → hit rect=17 (left edge)
─── Step 56 / 110 ─────────────────────────
[ccw #56] ALIGNED @ tile(11.2, 1.2) | R=3 L=2 | ray: BLOCKED ✗ → hit rect=17 (right edge)
─── Step 57 / 110 ─────────────────────────
[ccw #57] ALIGNED @ tile(12.2, 1.2) | R=3 L=2 | ray: BLOCKED ✗ → hit rect=17 (right edge)
─── Step 58 / 110 ─────────────────────────
[ccw #58] ALIGNED @ tile(13.2, 1.2) | R=3 L=2 | ray: BLOCKED ✗ → hit rect=17 (right edge)
─── Step 59 / 110 ─────────────────────────
[ccw #59] ALIGNED @ tile(14.2, 1.2) | R=3 L=2 | ray: BLOCKED ✗ → hit rect=17 (right edge)
─── Step 60 / 110 ─────────────────────────
[ccw #60] INTERSECTION @ tile(14.8, 1.2) | R=4 L=2 | ray: BLOCKED ✗ → hit rect=17 (right edge)
─── Step 61 / 110 ─────────────────────────
[ccw #61] ALIGNED @ tile(14.8, 2.2) | R=4 L=2 | ray: BLOCKED ✗ → hit rect=21 (right edge)
─── Step 62 / 110 ─────────────────────────
[ccw #62] ALIGNED @ tile(14.8, 3.2) | R=4 L=2 | ray: BLOCKED ✗ → hit rect=21 (right edge)
─── Step 63 / 110 ─────────────────────────
[ccw #63] ALIGNED @ tile(14.8, 4.2) | R=4 L=2 | ray: BLOCKED ✗ → hit rect=34 (right edge)
─── Step 64 / 110 ─────────────────────────
[ccw #64] INTERSECTION @ tile(14.8, 4.8) | R=5 L=2 | ray: BLOCKED ✗ → hit rect=34 (right edge)
─── Step 65 / 110 ─────────────────────────
[ccw #65] ALIGNED @ tile(13.8, 4.8) | R=5 L=2 | ray: BLOCKED ✗ → hit rect=34 (right edge)
─── Step 66 / 110 ─────────────────────────
[ccw #66] CORNER @ tile(12.8, 4.8) | R=5 L=3 | ray: BLOCKED ✗ → hit rect=34 (right edge)
─── Step 67 / 110 ─────────────────────────
[ccw #67] ALIGNED @ tile(12.8, 6.2) | R=5 L=3 | ray: BLOCKED ✗ → hit rect=40 (right edge)
─── Step 68 / 110 ─────────────────────────
[ccw #68] ALIGNED @ tile(12.8, 7.2) | R=5 L=3 | ray: BLOCKED ✗ → hit rect=40 (bottom edge)
─── Step 69 / 110 ─────────────────────────
[ccw #69] CORNER @ tile(12.8, 8.2) | R=5 L=4 | ray: BLOCKED ✗ → hit rect=45 (right edge)
─── Step 70 / 110 ─────────────────────────
[ccw #70] ALIGNED @ tile(14.2, 8.2) | R=5 L=4 | ray: BLOCKED ✗ → hit rect=53 (left edge)
─── Step 71 / 110 ─────────────────────────
[ccw #71] INTERSECTION @ tile(14.8, 8.2) | R=6 L=4 | ray: BLOCKED ✗ → hit rect=52 (right edge)
─── Step 72 / 110 ─────────────────────────
[ccw #72] JUMPED to new obstacle group rect=66 edge=1 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=66 edge=1 — CHECK if this is a boundary rect!
─── Step 73 / 110 ─────────────────────────
[ccw #73] CORNER @ tile(14.2, 8.8) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=52 (bottom edge)
─── Step 74 / 110 ─────────────────────────
[ccw #74] ALIGNED @ tile(12.8, 8.8) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=45 (right edge)
─── Step 75 / 110 ─────────────────────────
[ccw #75] ALIGNED @ tile(11.8, 8.8) | R=0 L=1 | ray: BLOCKED ✗ → hit rect=51 (right edge)
─── Step 76 / 110 ─────────────────────────
[ccw #76] CORNER @ tile(10.8, 8.8) | R=0 L=2 | ray: BLOCKED ✗ → hit rect=58 (right edge)
─── Step 77 / 110 ─────────────────────────
[ccw #77] INTERSECTION @ tile(10.8, 9.8) | R=1 L=2 | ray: BLOCKED ✗ → hit rect=63 (right edge)
─── Step 78 / 110 ─────────────────────────
[ccw #78] INTERSECTION @ tile(10.2, 9.8) | R=2 L=2 | ray: BLOCKED ✗ → hit rect=58 (bottom edge)
─── Step 79 / 110 ─────────────────────────
[ccw #79] ALIGNED @ tile(10.2, 8.8) | R=2 L=2 | ray: BLOCKED ✗ → hit rect=51 (bottom edge)
─── Step 80 / 110 ─────────────────────────
[ccw #80] ALIGNED @ tile(10.2, 7.8) | R=2 L=2 | ray: BLOCKED ✗ → hit rect=45 (bottom edge)
─── Step 81 / 110 ─────────────────────────
[ccw #81] ALIGNED @ tile(10.2, 6.8) | R=2 L=2 | ray: BLOCKED ✗ → hit rect=39 (bottom edge)
─── Step 82 / 110 ─────────────────────────
[ccw #82] INTERSECTION @ tile(10.2, 6.2) | R=3 L=2 | ray: BLOCKED ✗ → hit rect=40 (left edge)
─── Step 83 / 110 ─────────────────────────
[ccw #83] CORNER @ tile(11.2, 6.2) | R=3 L=3 | ray: BLOCKED ✗ → hit rect=39 (right edge)
─── Step 84 / 110 ─────────────────────────
[ccw #84] ALIGNED @ tile(11.2, 4.8) | R=3 L=3 | ray: BLOCKED ✗ → hit rect=33 (right edge)
─── Step 85 / 110 ─────────────────────────
[ccw #85] CORNER @ tile(11.2, 3.8) | R=3 L=4 | ray: BLOCKED ✗ → hit rect=26 (right edge)
─── Step 86 / 110 ─────────────────────────
[ccw #86] INTERSECTION @ tile(10.2, 3.8) | R=4 L=4 | ray: BLOCKED ✗ → hit rect=26 (left edge)
─── Step 87 / 110 ─────────────────────────
[ccw #87] ALIGNED @ tile(10.2, 2.8) | R=4 L=4 | ray: BLOCKED ✗ → hit rect=21 (left edge)
─── Step 88 / 110 ─────────────────────────
[ccw #88] ALIGNED @ tile(10.2, 1.8) | R=4 L=4 | ray: BLOCKED ✗ → hit rect=17 (left edge)
─── Step 89 / 110 ─────────────────────────
[ccw #89] INTERSECTION @ tile(10.2, 1.2) | R=5 L=4 | ray: BLOCKED ✗ → hit rect=17 (left edge)
─── Step 90 / 110 ─────────────────────────
[ccw #90] ALIGNED @ tile(11.2, 1.2) | R=5 L=4 | ray: BLOCKED ✗ → hit rect=17 (right edge)
─── Step 91 / 110 ─────────────────────────
[ccw #91] ALIGNED @ tile(12.2, 1.2) | R=5 L=4 | ray: BLOCKED ✗ → hit rect=17 (right edge)
─── Step 92 / 110 ─────────────────────────
[ccw #92] ALIGNED @ tile(13.2, 1.2) | R=5 L=4 | ray: BLOCKED ✗ → hit rect=17 (right edge)
─── Step 93 / 110 ─────────────────────────
[ccw #93] ALIGNED @ tile(14.2, 1.2) | R=5 L=4 | ray: BLOCKED ✗ → hit rect=17 (right edge)
─── Step 94 / 110 ─────────────────────────
[ccw #94] INTERSECTION @ tile(14.8, 1.2) | R=6 L=4 | ray: BLOCKED ✗ → hit rect=17 (right edge)
─── Step 95 / 110 ─────────────────────────
[ccw #95] ALIGNED @ tile(14.8, 2.2) | R=6 L=4 | ray: BLOCKED ✗ → hit rect=21 (right edge)
─── Step 96 / 110 ─────────────────────────
[ccw #96] ALIGNED @ tile(14.8, 3.2) | R=6 L=4 | ray: BLOCKED ✗ → hit rect=21 (right edge)
─── Step 97 / 110 ─────────────────────────
[ccw #97] ALIGNED @ tile(14.8, 4.2) | R=6 L=4 | ray: BLOCKED ✗ → hit rect=34 (right edge)
─── Step 98 / 110 ─────────────────────────
[ccw #98] INTERSECTION @ tile(14.8, 4.8) | R=7 L=4 | ray: BLOCKED ✗ → hit rect=34 (right edge)
─── Step 99 / 110 ─────────────────────────
[ccw #99] ALIGNED @ tile(13.8, 4.8) | R=7 L=4 | ray: BLOCKED ✗ → hit rect=34 (right edge)
─── Step 100 / 110 ─────────────────────────
[ccw #100] CORNER @ tile(12.8, 4.8) | R=7 L=5 | ray: BLOCKED ✗ → hit rect=34 (right edge)
─── Step 101 / 110 ─────────────────────────
[ccw #101] ALIGNED @ tile(12.8, 6.2) | R=7 L=5 | ray: BLOCKED ✗ → hit rect=40 (right edge)
─── Step 102 / 110 ─────────────────────────
[ccw #102] ALIGNED @ tile(12.8, 7.2) | R=7 L=5 | ray: BLOCKED ✗ → hit rect=40 (bottom edge)
─── Step 103 / 110 ─────────────────────────
[ccw #103] CORNER @ tile(12.8, 8.2) | R=7 L=6 | ray: BLOCKED ✗ → hit rect=45 (right edge)
─── Step 104 / 110 ─────────────────────────
[ccw #104] ALIGNED @ tile(14.2, 8.2) | R=7 L=6 | ray: BLOCKED ✗ → hit rect=53 (left edge)
─── Step 105 / 110 ─────────────────────────
[ccw #105] INTERSECTION @ tile(14.8, 8.2) | R=8 L=6 | ray: BLOCKED ✗ → hit rect=52 (right edge)
─── Step 106 / 110 ─────────────────────────
[ccw #106] ALIGNED @ tile(14.8, 9.2) | R=8 L=6 | ray: BLOCKED ✗ → hit rect=66 (right edge)
─── Step 107 / 110 ─────────────────────────
[ccw #107] ALIGNED @ tile(14.8, 10.2) | R=8 L=6 | ray: BLOCKED ✗ → hit rect=66 (right edge)
─── Step 108 / 110 ─────────────────────────
[ccw #108] ALIGNED @ tile(14.8, 11.2) | R=8 L=6 | ray: BLOCKED ✗ → hit rect=73 (right edge)
─── Step 109 / 110 ─────────────────────────
[ccw #109] JUMPED to new obstacle group rect=82 edge=1 (boundary rect? check if this is the bug)
  ⚠ Walk terminated: jumped
  → Jumped to rect=82 edge=1 — CHECK if this is a boundary rect!

Again, I’ll be back diving into more depth into your answer this weekend.

That does look interesting, the second example seems to show the same path points being searched twice, but usually astar has a “closed” list and an “open” priority queue. The nodes that had already been searched would be in the closed list and therefore skipped. The search would typically just follow another direction or hop back to an earlier point in the search tree and start out in another direction.

The search seems to go over old ground in video 3 as well, im not 100% sure that isnt what is supposed to happen. I had to zoom in on the videos to see the actual node thats being searched.

I see now, example 2 might have the hybrid vector approach where the agent hops to the boundary?

Then when the agent reaches the boundary it searches the path already found causing a waste of time - you should access the closed list with

 if not closed.contains(node):

to check that the path isnt following an already exhausted avenue of search.

Also the vector approach just casts a simple ray to the target, when it hits an obstacle it branches, check left and right and walk the boundaries casting rays until its visible … there might be snag cases bit for most obstacles that could work.

Maybe obstacles have data … you collide the ray then retrieve obstacle data, including the boundary, its easy to find the edge viewed from the start, then check from the edge of the boundary for a clear line of sight.

And casting through the obstacle should hit two boundaries, the near and far, then take the intersection with the far boundary and try to cast a ray to goal …

Thanks for the suggestion. Could you elaborate a bit on how that would look? It’s all very new to me and I’m starting to feel I chewed off more than I can do at the moment.

My current implementation seems to work sometimes, but it has so many weird behaviours that I feel like starting over with the knowledge I’ve gained would be a cleaner way to move forward.

If there are better methods than A* and/or edgewalking with raycasting, that are deterministic I’m all ears.

Alternatively I might just focus op epic 3 first (Placeholder buildings and building placement) to give myself a break from pathfinding.

Vector based just casts rays left and right until theres a gap then continues from the gap. I would say it isnt easy for strategy AI and the maps would have to be more simple. The idea can be combined with precomputed paths or navigation points with relaxed (non strict) path following.

About the boundary around the buildings - if you number the nodes from 0-count its easy to calculate the quickest way around the building.

Also, once you have a path with A then you should be able to construct it walking back from the goal. If you make a hybrid with a ray cast then you have to add the path the ray covered to the closed list and do not reset the Astar between raycasts on the same search.

The additional ray cast should link the nodes properly to the searched nodes in the astar search so that the path can be constructed.

Just happened to stumble across this a bit late but the error is in when you do the ray casting in the algorithm:
Shrink the unit down to a point, and apply a margin equal the size of the shrunken player to obstructions.

  1. Shoot a ray from the unit to the destination. If it hits the goal, you’ve got a clear path. If it doesn’t, you’ve found an obstacle. Start walking the edge of the obstacle.
  2. At every vertex, go back to step 2. ← this you want to do only after you have walked the full shape of the obstracle. Then you basically have knowledge of all the vertices and you can assign them a cost with a heuristic so you can prioritize/cull most of them. When you hit an obstacle for the second time it gets a tiny bit more complicated as you do not want to do all the work again. Instead you want to lookup the vertices costs and see if your path now is shorter and then reprioritize the nodes
  3. Repeat until you have a clear path
  4. Brute force smooth the path.

Besides the error, I cannot recommend going down this path to be honest :sweat_smile: like I mentioned at the start of the talk this is really only something you should do if you have no other options or just for fun perhaps.

Thanks for pitching in, appreciate it.

I already kinda dropped this approach and set up a temporary solution for the time being. Pathfinding is definitely something I’ll have to revisit at a later stage.

Can you elaborate why?

Other approaches are less of a headache and or preferred by players. For example the swarm like pathfinding from Starcraft 2

I was under the impression that, besides a few quirks, people generally like the AOE2 pathfinding because of it’s predictability. But thanks for chipping in. I might reevaluate my approach when I revisit pathfinding later :raising_hands: