0 votes

Hi everyone,

I am currently working on a tilemap, bigger than screen where I manage to move my player thanks to many existing tutorials.

What I am also trying to do, is to have NPCs moving using the same process from specific points (I do not want them to just wander everywhere, but let say, from cities to cities).

I believe I face two problems :
- I need to create path for every character even when the player is not on the tilemap
- I want them to move while my tilemap scene is off (Player is not on the world map)

For this matter, I created a dictionary on a one-shot effort (but it can be updated) that is supposed to provide for each cities, the distance with the other existing cities.

Nonetheless, it seems I am facing some troubles with the getsimplepath between two cities.
I tried to use it between tilemap cells coordinate. Between maptoworld coordinate and alway get an empty path...

I am certainly doing something wrong here... but I am wondering if the getsimplepath is actually able to calculate a path/distance or if I should determine the distance between to cities in a whole different fashion...

if it is of any use, I am doing the following :

This function search for a city on my map. When it finds one, it launches another function looking for relatives distances between this city an all other "visitable" ( = cities) tiles.

func city_distances():
    var origin_cell = Vector2()
    for x in range(map_size.x):
        origin_cell.x=x
        for y in range (map_size.y):
            origin_cell.y=y
            if $Navigation2D/TileMap.tile_dic[origin_cell].visitable == true:
                Global.dic_cities_distances[$Navigation2D/TileMap.tile_dic[origin_cell].name]=all_cities_distance(origin_cell)

The following function is called whenever a cities is found, it crawls the tilemap in search for any other cities, and is supposed to provide a path between the first city and any other cities. The path lenght (which my be a bad idea) is supposed to give me an idea regarding the distances between the two said cities:

func all_cities_distance(origin_cell): #from one cell, crawl the tile_dic to find distances for all cities
    var temp_city_dist={}
    var target_cell = Vector2()
    for i in range(map_size.x):
        target_cell.x=i
        for j in range (map_size.y):
            target_cell.y=j
            var path = PoolVector2Array()
            if $Navigation2D/TileMap.tile_dic[target_cell].visitable == true && target_cell!=origin_cell:
#path = $Navigation2D.get_simple_path(origin_cell,target_cell,false) #attempt with cell coordinate
                path = $Navigation2D.get_simple_path($Navigation2D/TileMap.map_to_world(origin_cell),$Navigation2D/TileMap.map_to_world(target_cell),false) #attempt with map to world ..
                temp_city_dist[$Navigation2D/TileMap.tile_dic[target_cell].name]={"Distance":path.size()}
    return temp_city_dist

Result is a nice dictionary with Distance:0 between all cities.... (path remains empty while debugging).

At this point, I am considering all options :D Including different ways to get the distance between two tiles from a tilemap....

Many thanks for your help/questions/hints.

Best regards,

Bk

Godot version 3.2.3
in Engine by (80 points)

1 Answer

0 votes

(path remains empty while debugging).

That indicates to me that your Navigation2D node isn't set up properly. It seems to me that you are calling get_simple_path() with the correct arguments. Maybe try calling it with global positions and see if that gets you a path?

var tmap = $Navigation2D/TileMap
var origin_local = tmap.map_to_world(origin_cell)
var target_local = tmap.map_to_world(target_cell)
path = $Navigation2D.get_simple_path(tmap.to_global(origin_local), tmap.to_global(target_local),false)

Here is a video demonstrating using a Navigation2D node on a tilemap: https://www.youtube.com/watch?v=0fPOt0Jw52s

Here is a video on general path finding that also shows the alternative AStar node path finding: https://www.youtube.com/watch?v=Ad6Us73smNs
The video is a bit older, since it was created Godot added a AStar2D node where you can work entirely with Vector2's instead of converting to Vector3's. It's very easy to download the code from that video and update it to use AStar2D.

by (3,375 points)
edited by

Ok, so I tried while adding half cells but failed to get results. (path.size() always == 0)

What I am actually trying is to get a path for npc at the same moment I get one for the player. Trying to send it in a random location from there.
It requires a little bit of set up as I want to be able to switch back to a more desirable setting once I get what is wrong.
I might get a good run tomorrow...

A little bit more on this.

I hard coded "local coordinates" and tried to see if I could get a path.

here is the chunk of code (it is ugly but I want to solve this ^^') :

    var vector1 = Vector2()
    vector1.x = 650
    vector1.y = 400
    var vector2 = Vector2()
    vector2.x=800
    vector2.y=600
    var temp_path = $Navigation2D.get_simple_path(vector1,vector2,false)
    print("temp path : ", temp_path)

In the same script, are my player movement on click and my paths generator.
Went if copy this in my player movement event I get something :

temp path : [(650, 400), (736, 437.5), (806, 542.5), (800, 600)]

While my loop in the path_generator just...gives nothing :

temp path : [] path : [] temp path : [] path : [] temp path : [] path
: [] temp path : [] path : []

I am starting to feel there is something awkward and/or not ready when I call the path generator. So I will try to call it at the same moment I move my player to see if it changes anything...

ok so it worked when I called the city_dstances() at the end of the player movement func.

temp path : [(650, 400), (736, 437.5), (806, 542.5), (800, 600)] path
: [(351, 1732.5), (316, 1697.5), (246, 1592.5), (246, 1487.5), (316,
1382.5), (386, 1277.5), (456, 1172.5), (526, 1067.5), (596, 962.5), (596, 857.5), (596, 752.5), (666, 647.5), (666, 542.5), (666, 437.5),
(701, 367.5)] temp path : [(650, 400), (736, 437.5), (806, 542.5),
(800, 600)] path : [(351, 1732.5), (421, 1750), (526, 1697.5), (596,
1592.5), (666, 1487.5), (736, 1382.5), (806, 1277.5), (876, 1172.5), (946, 1067.5), (1051, 1015), (1156, 962.5), (1226, 857.5), (1331,
805), (1471, 805), (1611, 805), (1681, 787.5)] temp path : [(650,
400), (736, 437.5), (806, 542.5), (800, 600)]

I was initially calling city_distances() at the end of the _ready():

func _ready():
    $Character.show()
    $floating_Label.hide()
    map_size=Global.map_size
    is_hovering = false
    if Global.dic_cities_distances.size()==0: #if it has not previously been done, populates the city_distances dictionary. Has to be reperformed if a new city is added
         city_distances()

I do not get it as my code is not preparing anything fancy nor initiates things for the $Navigation2D to work on click.

I would like to understand this..

From my understanding, the nodes at the bottom of the hierarchy have their _ready() functions called first then it goes up the tree from there. I would bet that Navigation2D nodes need to have their _ready() functions called before they will work properly. You can test which node is ready first pretty easily. Add this code to the node where you were calling city_distances() from _ready() (replacing the node path in get_node() to match yours, you can drag and drop the path from the scene tree on the left):

extends Node2D

var my_cool_var

func _ready() -> void:
    my_cool_var = 'node'
    print('nav is inside tree: ',  get_node("../Navigation2D").is_inside_tree())
    print('nav is ready: ', get_node("../Navigation2D").my_cool_var != null)

and add this code to your Navigation2D script (again replacing the node path in get_node() to match yours):

extends Navigation2D

var my_cool_var

func _ready() -> void:
    my_cool_var = "nav"
    print('node is inside tree: ',  get_node("../Node2D").is_inside_tree())
    print('node is ready: ', get_node("../Node2D").my_cool_var != null)

Hi, I am a little bit confused about what this is supposed to achieve.

I think I have enought material to get around my problem, but when I tried you thing is crashes stating I cannot call isinsidetree" on a null instance from my navigation2D.

There is nothing (no script) in my navigation2D so I added just this.

Is it a cross checking variable (navigation calls node's variable and tries to display it, while the node tries to call navigation's variable ?)

I will try to just go with distance calculus and check that on a given timer, my NPC movements func manages to get paths. Hope it works!

Sincerely thank you for you time and help !

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.