Identifying map tile with mouse click

I am planning on doing a warcraft3-like RTS, and for that I would like to divide the terrain (in the early versions it will be a plain map without accidents in the z direction) in tiles. These tiles will then be used for pathfinding by telling if a map section is or not inaccessible (when there are trees or buildings at the location the tile will be marked). The representation should be simple as a 2-dimensional matrix, but I have some doubts as how to accomplish it:
a) Could anyone give a suggestion as how to map the terrain in the matrix? b) One problem is that when I click somewhere on the terrain I should obtain the x-y global coordinates of the place so then I can identify the tile that was selected (the tiles will be mapped on global x-y coordinates). Using the collision ray trick from the manual is there a way to detect the coordinates of the collision with the terrain?
c) Instead of mapping into the matrix a terrain with all the obstacles already set, these could be added procedurally (as in the case of “random maps”). Would this be a better approach?

a) You could use Panda’s scene graph I think. At one level in the scene graph, you could have a NodePath representing each cell of your terrain matrix. The actual geometry of the terrain would be attached to these NodePaths. You could then store references to these NodePaths in a 2-dimensional Python list to represent your (x,y) matrix of cells. You need to set the position of each of these nodepaths accordingly, ofcourse.

b) This is a common technique in Panda called ‘picking’ which I think you can find in the manual, where you shoot a collision ray from the mouse pointer into the screen and see what it collides with. This lets you figure out what object in the 3D scene the mouse is pointing at. Panda’s collision system will provide you with a NodePath, the object that the ray collided with. Given this collision point, you can move up the scene graph until you reach one of the NodePaths of your (x,y) matrix, and this will tell you which tile is being pointed at. You probably want to tag the nodepaths of your (x,y) matrix so you can find them.

c) Up to you.

Shouldn’t the tile nodepaths be independent from the actual terrain? I don’t know how it would work: first the collision would be detected with the actual terrain, but how can I reach a specific tile nodepath from this? The collision would only return the terrain nodepath.

I believe I should associate a square-like collision solid to each tile nodepath in order to detect the collisions… Is it a good approach? But then, the tile nodepaths would bear no relation to the terrain nodepath (except maybe for a direct parent-child relationship).

you should indeed make the ai stuff independend from the terrain geometry.
first of all. using many small tiles for a terrain is a bad idea (it can break performance BIG time). second one. it’s nasty do deal with.

guess easiest approach is to have terrain and your routing seperate from each other.
to get where you clicked, just get the intersection point relative to a corner of your terrain.
you could use getSurfacePoint or somthing suitable.
this might return a x,y,z value such as (147.12 , 42,16 , 142.1)
now you just have to do something like

 x = int( returnedXValue \\ yourTileSize)

same for y and you already have the tile in question which can be used to lookup on a list or something where you stored your free and blocked pathes

You’re right, but given this terrain nodepath, you can travel up the scene graph by taking the parent nodepath, until you find one of your tile nodepaths. This is a common Panda technique. How do you know when you’ve reached a tile nodepath? Because you attach tags to your tile nodepaths to identify them. The code would look something like this (where nodepath is originally the terrain nodepath returned to you by the collision:

            while nodepath != render:
                if nodepath.hasTag('tile node'):
                    tile = nodepath
                    break

To set the tag by the way, use nodepath.setTag(“key”,“value”). You can store string values in tags also, and if you use setPythonTag and getPythonTag instead, you can store any Python object in a tag. So you could, for example, store the (x,y) coordinates of a tile nodepath in your matrix as a tag on the nodepath object itself.

As for collision geometry versus visible geometry, I am not sure what would be best. Not an expert on Panda’s collision system. If you were doing it my way, then each of your tile nodepaths would have some visible geometry and some collision geometry below it in the scene graph. But I suppose you could also do it another way and create the visible geometry then create the collision geometry and synchronise their positions.

A number of people who frequent these forums have used and even written terrain generators and renderers in Panda (include me, actually, but I’ve forgotten it all). One of them should be able to give you some better advice about collision geometry.

Hmmm … ThomasEgi’s way is very simple and efficient, but my way your conceptual grid of tiles actually corresponds to a set of nodes in the scene graph, that could have advantages like you can easily do things like highlight a tile by dealing with the nodepath for that tile. But I suppose there are other ways to achieve such effects. Like I said, I’ve forgotten all about doing terrain in Panda, there are all sorts of very technical efficiency considerations which I’m ignoring right now.

i just quickly read the posts, but i think using the picking method is not the best.

If you have a large terrain it might require a huge amount of polygons to check. If the terrain is flat, you could create a plane and do a collision-mouse-ray check against that plane, using the getSurfacePoint function it would give you the coordinates of the collision. with these coordiantes you could calculate the tiles position. If it’s not flat but contains flat areas you could simplify the collision object using this method as well.

If you want to work with tiles. Make sure you only make the terrain, not any objects on top of it, collidable. using the settag function you can assign strings to the nodepath, this way you could directly save the position x/y into it.

ps: lots of posts in the meantime i wrote this, this might be redundant now.

The last code snippet I posted was missing a line. It should be:

            while nodepath != render:
                if nodepath.hasTag('tile node'):
                    tile = nodepath
                    break 
                nodepath = nodepath.getParent()