N-sided triggers
by Andrew Edmonds · in Torque Game Engine · 06/04/2006 (10:28 am) · 8 replies
Hi all,
Is it possible to create n-sided triggers? For example, I have a circular area (in fact it's a 16-sided area, but it's circular enough!) and I want to detect when a player is near the edge. I could do this by creating a series of triggers around the edge, but ideally would like to just create one 16-sided trigger.
Is this possible??
Thanks,
Mike
Is it possible to create n-sided triggers? For example, I have a circular area (in fact it's a 16-sided area, but it's circular enough!) and I want to detect when a player is near the edge. I could do this by creating a series of triggers around the edge, but ideally would like to just create one 16-sided trigger.
Is this possible??
Thanks,
Mike
About the author
Formed in 2005, EiKON Games is an indie games development project based in the UK working on the tactical first person shooter "Epoch: Incursion". See the Join Us or Contact Us pages at http://www.eikon-games.com/
#2
Now, if anyone can explain how that's done in source that would be great!
06/05/2006 (12:11 am)
Sounds like just the job - thanks Paul.Now, if anyone can explain how that's done in source that would be great!
#3
Do a vectordist of the players position and the center of the circular area.
if the distance is less then the distance from the center of the cirlce to the edge of the circle, you're inside the circular area.
06/05/2006 (3:50 am)
Well here's an idea. make a normal trigger that's a little bigger then the sphere you want. and have the trigger check to see how far away you are from the center of the "circular area"Do a vectordist of the players position and the center of the circular area.
if the distance is less then the distance from the center of the cirlce to the edge of the circle, you're inside the circular area.
#4
06/05/2006 (7:08 am)
Aha! That makes perfect sense... Thinking outside the box (as it were). Thanks Ramen - I'll give that a try tonight...
#5
There are a few issues with this problem.
First of all - triggers, believe it or not, do represent themselves as a sphere... kind of. As far as collision goes, it's the objects that move that decide how exactly they represent themselves and how they respond to collision.
In the case of the trigger object, objects first check against it's bounding box by default, then it does more specific checks. In the more specific checks (see Trigger::testObject) the trigger builds a sphere and then calls a buildPolyList on the object in question. However in the buildPolyList function, all the object has to do is return a polygon list, and it may or may not do any clipping against the object's box, or sphere passed into it (such as the trigger's sphere). The player object simply returns a polygon list that's a box, regardless of the trigger sphere/box.
So how exactly is this a more specific check in terms of collision?
Well it comes to the polyList paramater used. Trigger's use an EarlyOutPolyList. An EarlyOutPolyList's responsibilities are to quickly check to see if there's at least ONE polygon added to the list that's inside of the area set by the owner of the polyList, in the trigger object it's specifically
So a perfect solution to this problem is to create your OWN concrete class of AbstractPolyList, that's like an EarlyOutPolyList, except it's a sphere - say EarlyOutSpherePolyList, and it checks if ANY polygon is in the sphere (probably not nearly as bad as you think, as long as one point is inside the sphere, so is the entire polygon), and use that instead of the EarlyOutPolyList for the trigger.
There's a less perfect solution however...
The only other solution I can think up of is set the two objects to explicitly represent themselves as spheres and see if they collide when the more specific checking is done. This may seem like a bit of hack - but I think it'll work ;).
This new problem isn't as intuitive as you might think, but the changes are simple nontheless.
Here's what I would do (in the engine) :
First, add a boolean mIsSphere field to your trigger object, so you can toggle between forcing sphere collision and box collision in the editor (see the addField function calls in initPersistFields).
Also add a radius field (e.g. F32 mRadius) and add it to the list of fields as well (see the addField function calls in initPersistFields). Make sure both are transmitted through the network in pack/unpackUpdate
Now the less intuitive part is how the trigger represents itself for collision.
Moving objects check against a convex that the trigger returns - in this case by default it's a box. Which you might think is a problem if you want to be checking with a sphere. But this isn't the case. If the object collides with the box, Trigger::potentialEnterObject(Gamebase* enter) is called on the trigger in question. This does more specific checks to see whether or not you've collided.
So...
For the collision to register properly:
First the box you initially build as a convex needs to encapsulate your sphere.
In void Trigger::buildConvex(const Box3F& box, Convex* convex) near the end of the method...
change
to
Mindboggling eh? :P
And then finally... the last bit comes when potentialEnterObject is called - it calls testObject to see whether or not they've actually collided - a more specific check than the boxes themselves.
In bool Trigger::testObject(GameBase* enter)...
change
to
- Eric
P.S. Yes - this problem wasn't as intuitive as I thought it was going to be :P
06/05/2006 (8:31 am)
@Mike:There are a few issues with this problem.
First of all - triggers, believe it or not, do represent themselves as a sphere... kind of. As far as collision goes, it's the objects that move that decide how exactly they represent themselves and how they respond to collision.
In the case of the trigger object, objects first check against it's bounding box by default, then it does more specific checks. In the more specific checks (see Trigger::testObject) the trigger builds a sphere and then calls a buildPolyList on the object in question. However in the buildPolyList function, all the object has to do is return a polygon list, and it may or may not do any clipping against the object's box, or sphere passed into it (such as the trigger's sphere). The player object simply returns a polygon list that's a box, regardless of the trigger sphere/box.
So how exactly is this a more specific check in terms of collision?
Well it comes to the polyList paramater used. Trigger's use an EarlyOutPolyList. An EarlyOutPolyList's responsibilities are to quickly check to see if there's at least ONE polygon added to the list that's inside of the area set by the owner of the polyList, in the trigger object it's specifically
mClippedList.mPlaneList = mTriggerPolyhedron.planeList;that sets the area of the EarlyOutPolyList. So as the player object for example adds it's polygons to the list, all the list is doing is checking to see if at least one of the polygons is actually inside it's bounds. A call to isEmpty() checks the results.
So a perfect solution to this problem is to create your OWN concrete class of AbstractPolyList, that's like an EarlyOutPolyList, except it's a sphere - say EarlyOutSpherePolyList, and it checks if ANY polygon is in the sphere (probably not nearly as bad as you think, as long as one point is inside the sphere, so is the entire polygon), and use that instead of the EarlyOutPolyList for the trigger.
There's a less perfect solution however...
The only other solution I can think up of is set the two objects to explicitly represent themselves as spheres and see if they collide when the more specific checking is done. This may seem like a bit of hack - but I think it'll work ;).
This new problem isn't as intuitive as you might think, but the changes are simple nontheless.
Here's what I would do (in the engine) :
First, add a boolean mIsSphere field to your trigger object, so you can toggle between forcing sphere collision and box collision in the editor (see the addField function calls in initPersistFields).
Also add a radius field (e.g. F32 mRadius) and add it to the list of fields as well (see the addField function calls in initPersistFields). Make sure both are transmitted through the network in pack/unpackUpdate
Now the less intuitive part is how the trigger represents itself for collision.
Moving objects check against a convex that the trigger returns - in this case by default it's a box. Which you might think is a problem if you want to be checking with a sphere. But this isn't the case. If the object collides with the box, Trigger::potentialEnterObject(Gamebase* enter) is called on the trigger in question. This does more specific checks to see whether or not you've collided.
So...
For the collision to register properly:
First the box you initially build as a convex needs to encapsulate your sphere.
In void Trigger::buildConvex(const Box3F& box, Convex* convex) near the end of the method...
change
// Create a new convex. BoxConvex* cp = new BoxConvex; mConvexList->registerObject(cp); convex->addToWorkingList(cp); cp->init(this); mObjBox.getCenter(&cp->mCenter); cp->mSize.x = mObjBox.len_x() / 2.0f; cp->mSize.y = mObjBox.len_y() / 2.0f; cp->mSize.z = mObjBox.len_z() / 2.0f;
to
// Create a new convex.
BoxConvex* cp = new BoxConvex;
mConvexList->registerObject(cp);
convex->addToWorkingList(cp);
cp->init(this);
if(mIsSphere) //Use the sphere's radius for the box
{
mObjBox.getCenter(&cp->mCenter);
cp->mSize.x = mRadius;
cp->mSize.y = mRadius;
cp->mSize.z = mRadius;
}
else
{
mObjBox.getCenter(&cp->mCenter);
cp->mSize.x = mObjBox.len_x() / 2.0f;
cp->mSize.y = mObjBox.len_y() / 2.0f;
cp->mSize.z = mObjBox.len_z() / 2.0f;
}Mindboggling eh? :P
And then finally... the last bit comes when potentialEnterObject is called - it calls testObject to see whether or not they've actually collided - a more specific check than the boxes themselves.
In bool Trigger::testObject(GameBase* enter)...
change
if (mTriggerPolyhedron.pointList.size() == 0)
return false;
mClippedList.clear();
SphereF sphere;
sphere.center = (mWorldBox.min + mWorldBox.max) * 0.5;
VectorF bv = mWorldBox.max - sphere.center;
sphere.radius = bv.len();
enter->buildPolyList(&mClippedList, mWorldBox, sphere);
return mClippedList.isEmpty() == false;to
if (mTriggerPolyhedron.pointList.size() == 0 && !mIsSphere)
return false;
mClippedList.clear();
SphereF sphere;
sphere.center = (mWorldBox.min + mWorldBox.max) * 0.5;
VectorF bv = mWorldBox.max - sphere.center;
sphere.radius = bv.len();
if(mIsSphere) // Check against the object's bounding sphere - not a polylist
{
SphereF testSphere; // This is the object we're testing against's radius
Point3F testCenter; // get the center
enter->getWorldBox().getCenter(&testCenter);
testSphere.center = testCenter;
VectorF objRadius = enter->getWorldBox().max - testSphere.center; // get the radius
testSphere.radius = objRadius.len();
sphere.radius = mRadius; // this is our object's radius
return sphere.isIntersecting(testSphere); // is it intersecting?
}
else
{
enter->buildPolyList(&mClippedList, mWorldBox, sphere);
return mClippedList.isEmpty() == false;
}- Eric
P.S. Yes - this problem wasn't as intuitive as I thought it was going to be :P
#6
Does anyone know what the "Polyhedron" field can be used for ?
typically it's set like this:
polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
06/05/2006 (9:15 am)
Eric - wow, nice post.Does anyone know what the "Polyhedron" field can be used for ?
typically it's set like this:
polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
#7
From the code comments in Trigger.cc:
So your first 3 digits are the origin, and the 3 other sets of 3 digits represent vectors of the edge of your quadrilateral.
- Eric
06/05/2006 (10:48 am)
@Evil Twin:From the code comments in Trigger.cc:
Quote:
The polyhedron type is really a quadrilateral and consists of a corner
point follow by three vectors representing the edges extending from the
corner.
So your first 3 digits are the origin, and the 3 other sets of 3 digits represent vectors of the edge of your quadrilateral.
- Eric
#8
so you could make various prisms with this,
but they have to be 6-sided..
not so useful.
thanks, orion.
06/05/2006 (11:05 am)
I see.so you could make various prisms with this,
but they have to be 6-sided..
not so useful.
thanks, orion.
Torque Owner Paul /*Wedge*/ DElia