Point & Click multi-level map

Greetings !

I’m currently working on a game on which the character moves when you click on some part of the map.

We’re using waypoints: we represent waypoints with spheric models, and their links with CollisionSegment (it’s not useful in the game itself, but it is in the map editor, to view the waypoints, and to determine if it’s physically plausible that a character can go from one waypoint to another).

Also, our maps can have several levels.

What we need is a system so that we can know which waypoint is the closest to where the mouse is at a given level.

We planned on using “bounding boxes” that would make sure that everywhere the user click is linked to at least one waypoint. The problem with that is that I’ve been unable to find the position of the mouse in the world.

But actually, this is the first time I’m doing anything like it, and I believe that I’m heading the wrong way.
So two questions here:

  • How would you find the x/y positions of the mouse in the world (the z is set by us arbitrarily).
  • How would you solve this problem ? (finding out which waypoint the user is trying to go to using a click)

Thanks !

As to finding the selected point, the manual suggests ray-picking; essentially, create a CollisionPlane at the appropriate position (parented to whatever makes most sense given your camera and intentions, I imagine), then attach a CollisionRay to the camera, traverse the plane, then interpret the collision (presuming that there is one).

Note that if you have only one object against which to check these collisions (that is, your picking plane), then you should be able to skip the tagging element given on the above-linked page, as you’re presumably interested not in what was picked but rather at what surface point the picking took place.

[edit]
As to the base problem, I suppose that my approach would likely depend on the camera and world being used:

  • If everything is 2D and attached to pixel2d, you might get away with just using mouse coordinates, or some transformed variation thereof.
  • If your game is only ever seen along one axis, and all clicks take place at a specific point along that axis, you might be able to use the collision plane method that I describe above.
  • If your game is viewed from some other perspective, but all events nevertheless take place on a plane, then you once again may be able to use the above-mentioned plane method. Note here, however, that your user experience may be reduced as a result of not being able to click on raised elements, such as the top of a dome.
  • Otherwise, you might go for the more general ray-picking system outlined in the page linked-to above.

Thanks ! That seems like a very good idea indeed, and with little work, the plane system can be used with multi-level maps.

I just tried to implement it and, at first run, I was surprised to see that it almost worked.
The puzzling thing is that it actually works, but only most of the time. Sometimes it fetches a waypoint miles away from where I clicked.

EDIT: Nevermind, it’s fixed \o/ !

Well, I’ve been struggling with planes for a while now.
I thought it worked (by cheer luck), but it turns out it totally doesn’t.

Soooo maybe you know how to create a plane ?
The CollisionPlane use an LPlane object, the thing is I have no clue how the LPlane object works…

It’s made out of a Vector4, which means only two points can be defined. This would work in a 2D environment, but we need at least one more coordinate in a 3D environment, don’t we ?

I found no information outside of the documentation (and it doesn’t explain which points are which).
So, I think I have almost everything ready for this system to work, but I just have no idea how to initialize the plane.

Looking at the manual and API reference, I see that Plane (or LPlanef, depending on whether you’re using 1.7.2 or 1.8.0, respectively), actually has five constructors, including one default constructor, and one copy constructor.

If you’re providing it with just a single Vec4, then you would seem to be using the copy constructor. Why this uses a Vec4 and not another (L)Plane(f) I’m not sure, but given what I see in the reference I’m guessing that it uses the four elements of the plane equation, A, B, C and D, simply packing those into a Vec4 rather than taking them separately.

Depending on your situation, you may find it more useful to use one of the other constructors, which seem to take one of:

  • Three points
  • The parameters of the plane equation, A, B, C and D, or
  • A normal and a point (in that order)

The API entries for (L)Plane(f): 1.7.2 and 1.8.0

By the way, I subsequently (re-)discovered this thread which gives an alternative to picking by collision. It still uses a plane, but is said in that thread to be likely to perform better than the collision-based version. It includes sample code, I believe.

The Plane class inherits from VBase4, since it stores only four parameters of the plane equation, which is:
Ax + Bx + Cx + D = 0
Some versions have it like this:
Ax + Bx + Cx = D
Which means that the D is negated.
These are the only four constants required to describe an infinite plane in 3D space.

There are two other ways of creating a plane, in both of these, Panda will calculate the appropriate plane coordinates:

Plane(Vec3 normal, Point3 point)
This version of the constructor takes the surface normal vector of the plane (basically, a vector perpendicular to the plane, pointing away from the front side), and a point on the plane. It needs to use the latter to find out how far the plane is from the origin, it can really be any point on the plane, since the plane extends infinitely in four directions.

Plane(Point3 a, Point3 b, Point3 c)
This one is probably easier to understand. You simply specify three points that lie on the plane (that aren’t equal to each other), and Panda will calculate the appropriate plane. The only caveat is that it’s somewhat ambiguous which side the plane faces. Panda defines the plane as follows:

//  Description: Constructs a plane given three counter-clockwise
//               points, as seen from the front of the plane (that is,
//               viewed from the end of the normal vector, looking
//               down).

A minor correction: That should be Ax + By + Cz + D = 0 and Ax + By + Cz = D, I think; you seem to have put ‘x’ in for all of the first three terms.

As to the inheritance from VBase4, fair enough, that makes sense. :slight_smile:

Well, kind sirs, it now works, thanks to that 3 Point3 counter-clockwise constructor.

Thanks for the enlightenments !

Any chance you could post your code or a sampling of how you implemented this?

I’m working with the “Roaming Ralph” sample code and trying to switch it from keyboard navigation to mouse-based, point & click navigation. Sadly, it’s going far slower than I’d hoped. Some new sample code would help tremendously!

Thanks.

If I recall correctly, Roaming Ralph uses an uneven terrain mesh, meaning that a plane might not work well for your intentions. However, since I seem to think that Roaming Ralph’s terrain has collision geometry (or is at least set up for collision with visible geometry), could you not simply fire a ray from the camera to the mouse position, find the intersection with the terrain, and use that as your target?