This is a pathfinder and A* solver (astar or a-star) native extension for the Defold Engine, built on MicroPather.
Some issues found.
In some cases solve_near() doesnāt return nears array after create a new map (switch between levels). But solve() still works correct. Sometimes solve_near() returns nears after second call only. First function call returns NO_SOLUTION and nil, second call after first one with the same map state returns SOLVED and nears table.
Also reset() or reset_cache() before map initialization will crash engine.
Iāll try to figure it out, but Iāll be glad if you can share a simple test case.
I guess it is because the cache. I have doubts that cache might not working as expected.
This is because Iām lazy and since Iām not expecting someone to reset things when it is empty Iāll take a look, It should be easy to fix.
Iām a bit confused, when Iāve tried to remove game assets and prepare vanilla tests - they works. So, I will investigate it more.
Perhaps, here is something wrong:
This code seems directly copied from Solve function and checking āpathToā, but which value āpathToā has within SolveNear function? Suppose this variable contains a value from previous calculation and in some case condition return NO_SOLUTION.
Highly possible. Honestly, I donāt remember why I did it this way. It seems very inappropriate right now.
Iām trying to complete one of my freelance work . It has a very tight deadline. Iāll check out those issues as soon as I can, sorry.
@Dragosha I fixed the issues, but as I suspected there is another issue about the cache. Iām going to push the changes when I find a way to fix this one(I hope before Monday).
I think all fixed now. You can test it from dev branch:
https://github.com/selimanac/defold-astar/archive/dev.zip
I was using astar.reset()
just for development purpose only and this is why I wasnāt exposed it to the documentation. It is different then astar.reset_cache()
. It removes all the data and the Map class internally. I fix it is bugs. It should be safe to use if you want to free some memory. But I believe it is not necessary. Also astar.reset()
delete the Costs array. It must be set. Otherwise proper error occur which describes the situation.
astar.reset_cache()
will not crash the engine anymore. Actually you donāt need to call it before changing map state using astar.set_map()
. astar.set_map()
calls reset cache internally.
You were right, It was about the world bound check for early exit. It is fixed now. (At least as far as I can test)
This is not related to your issue but It was returning nil
caused by other issue. It was returning SOLVED and nil
before when there is only tile itself as a solution. It will return NO_SOLUTION now.
There isnāt any breaking changes but I made a few more fix.
I try to test every circumstances which I can think of.
Here are the test files on dev branch:
I merged those fixes to āmasterā as v1.0.1.
If you are having an issue about those latest changes, please let me know.
astar.solve(start_x, start_y, end_x, end_y)
Worth noting that, start offset is 0 as oppose to Lua tables which starts from 1.
That is correct. It would be a good practice to change it to start with 1, but unfortunately I donāt have much spare time these days.
PS: There is also an issue and pull request about this. But this pull request isnāt backward compatible so I donāt want to merge it.:
I also forked this awesome extension and add two methods: astar.get_at(x
, y
) to read value by coordinates and astar.set_at(x
, y
, value
) to change map data without updating the whole map
@Dragosha This is great.
I can give you access to this repo if you want. We can merge this improvement.
I want to get advice from experienced developers who use this library.
Imagine, there are heroA and heroB types of characters, each character has its own pathfinding costs: costsA / costsB
map is a grid of 30x30 tiles and there are 5 mobs of group A, and 5 mobs of group B characters. characters move in real time. (not turn-based)
right now I see the following way:
every time before calling the function astar.solve(start_x, start_y, end_x, end_y)
call astar.set_costs(costsA)
or astar.set_costs(costsB)
Is it the right way to do? considering the factor of optimising the memory allocated for using this library and performance on weak devices? What is the best way to implement this?
and of course we know that: Setting new cost data resets the current cache.
so need to disable cache also.
As far as I remember this is the only way I can think of.
I donāt remember the code very well but MicroPatherās official doc says:
MicroPather does a lot of caching. You want to create one and keep in around. It will cache lots of information about your graph, and get faster as it is called. However, for caching to work, the connections between states and the costs of those connections must stay the same. (Else the cache information will be invalid.) If costs between connections does change, be sure to call Reset().
Edit: I think if your map is not so large then performance shouldnāt be a problem but unfortunately you have to test the every situations a specially on weak devices.
If youāre not keen on modifying the C++ code, and you find performance is an issue, you can make a copy of the extension (placing it in the root of your project directory), rename it, and use the copy for the second group. You need to change the name in ext.manifest
, plus LIB_NAME
and MODULE_NAME
at the top of astar.cpp
.
Of course, this gets unwieldy if you want to add a third or fourth group in the future. Itās more of a quick-and-dirty solution.
@selimanac thanks for this additional information!
@GooseSwanson thanks, interesting idea. i was thinking how an independent āclass instanceā could be used. anyway, I should run tests first.
Finally, I did some test.
results of 200000 (!) iterations. [the number of iterations on my PC when the difference in calculations began to increase]
find_path()
prefomance test took 0.41097068786621
prefomance test took 0.44680404663086
prefomance test took 0.41289520263672
prefomance test took 0.47176742553711
prefomance test took 0.43683242797852
set_costs() + find_path()
prefomance test took 0.51663398742676
prefomance test took 0.54156303405762
prefomance test took 0.53110122680664
prefomance test took 0.50229072570801
prefomance test took 0.56503105163574
conclusion:
In my case (30Ń
30 tiles), it doesnāt affect performance much.
here you can find a source of this perfomance-test project:
And just for fun; what is the device(s) specs?