Players walking on vehicles (a suggestion, not a question)
by Daniel Buckmaster · in Torque Game Engine · 07/25/2008 (2:23 am) · 11 replies
I've been doing some (a little) research on this, because it's something I'll need for my game. Solutions seemed to revolve around 'attaching' Players to a vehicle's coordinate space, then allowing them to move within that. This would require a way to keep track of what coordinate space different objects are in (like Ben Garney's SceneObject mounting').
But one person had a different idea, and I really liked this approach. I'll quote from Jonathan Toolan, because I can't say it any better myself:
I've worked up some proto code that I *think* will work. The idea is that in Player::findContact, when a convex is collided with, if it belongs to a Player or Vehicle, its velocity is stored. Then in Player::updateMove, this velocity is added to whatever we get through moving. (I added an inverse function as well, so as the difference in velocity increases, the veloity passed on decreases, allowing some slippage.)
I'm going to have a go at testing this. Just before I do:
-Is there any reason why this shouldn't work?
-Are there significant advantages of an attachment system, apart from the fact that it's more generally applicable?
-Correct me if I'm wrong - I don't believe any network code needs to be touched.
EDIT: One problem seems to be vehicle->player collision. Players stop vehicles dead. I assume this is a code problem, not a script problem - a mass 90 player should not stop a mass 200 buggy at full speed.
In starter.fps, if I create a buggy and jump on top of it, the buggy won't move at low speeds - when I set move speed to about 0.2, the buggy jerkily slides out from under the player. Not what I was looking for.
But one person had a different idea, and I really liked this approach. I'll quote from Jonathan Toolan, because I can't say it any better myself:
Quote:It seems to be the case (I haven't tested this - it's only based on what I've read) that friction is assumed. My idea of the torque model of friction could be expressed as: "A body in motion will come to rest if no keys are pressed." whereas a different model would be: "A body in motion will gradually assume the motion of the object under it's feet if no keys are pressed."This is simple and elegant, and can be implemented without attachment, or keeping track of lists ofobjects.
I've worked up some proto code that I *think* will work. The idea is that in Player::findContact, when a convex is collided with, if it belongs to a Player or Vehicle, its velocity is stored. Then in Player::updateMove, this velocity is added to whatever we get through moving. (I added an inverse function as well, so as the difference in velocity increases, the veloity passed on decreases, allowing some slippage.)
I'm going to have a go at testing this. Just before I do:
-Is there any reason why this shouldn't work?
-Are there significant advantages of an attachment system, apart from the fact that it's more generally applicable?
-Correct me if I'm wrong - I don't believe any network code needs to be touched.
EDIT: One problem seems to be vehicle->player collision. Players stop vehicles dead. I assume this is a code problem, not a script problem - a mass 90 player should not stop a mass 200 buggy at full speed.
In starter.fps, if I create a buggy and jump on top of it, the buggy won't move at low speeds - when I set move speed to about 0.2, the buggy jerkily slides out from under the player. Not what I was looking for.
About the author
Studying mechatronic engineering and computer science at the University of Sydney. Game development is probably my most time-consuming hobby!
#2
Lets say the vehicle is some sort of hovering type.
When it turns in place you run into problems.
A player riding on a vehicle will not maintain it's relative position when the vehicle turns.
Even a wheeled vehicle will exhibit this problem at any movement speed.
07/26/2008 (9:26 am)
I like your idea but I think one problem you are going to run into is rotation.Lets say the vehicle is some sort of hovering type.
When it turns in place you run into problems.
A player riding on a vehicle will not maintain it's relative position when the vehicle turns.
Even a wheeled vehicle will exhibit this problem at any movement speed.
#3
Still working on standing on vehicles... I think I'm just going to add a nominal skyward impulse and be done with it :P. But I really would like to actually get this working.
EDIT: Okay, bad idea. I don't know why, though :P. Next approach: using code similar to resolveDisplacement but using velocity/impulse methods as well as simple diplacement of objects. I'm going for the feel of resolveDisplacement - the car will push the player along, rather than having the player rocket back, but the player will retain momentum. If the collision normal is mostly parallel to the line between the colliding objects, a displacement may be added. This displacement is zero when the normal is perpendicular to velocity; it is greatest when the normal is parallel to velocity, but zero if the vectors are opposite.
I don't know how to explain that simply :P. But let's see if it works, first...
07/26/2008 (10:41 am)
Vehicle::getVelocity can take a Point3F argument, the relative position to the vehicle. So angular velocity will be taken into account. I think I just need to use that, but you're right, it could be a problem.Still working on standing on vehicles... I think I'm just going to add a nominal skyward impulse and be done with it :P. But I really would like to actually get this working.
EDIT: Okay, bad idea. I don't know why, though :P. Next approach: using code similar to resolveDisplacement but using velocity/impulse methods as well as simple diplacement of objects. I'm going for the feel of resolveDisplacement - the car will push the player along, rather than having the player rocket back, but the player will retain momentum. If the collision normal is mostly parallel to the line between the colliding objects, a displacement may be added. This displacement is zero when the normal is perpendicular to velocity; it is greatest when the normal is parallel to velocity, but zero if the vectors are opposite.
I don't know how to explain that simply :P. But let's see if it works, first...
#4
I'm having big trouble trying to make some nice collision resolution code :P...
EDIT: New approach, again. Looking at the rigid->immovable code, it uses getZeroImpulse to calculate an impulse that will stop the rigid entering the surface. I'm going to use the same method to calculate an impulse that will stop the player entering the vehicle.
EDIT: Another no-go. Well, I guess it's a semi-go. I don't understand whan invWorldInertia is (or at least how it is calculated and what it represents). But obviously Players don't have such a complicated physics system, so it's a bit daft to try to use it. I'm just going to have to figure out my own impulse calculations.
07/27/2008 (1:03 am)
One problem will be that the actual rotation of the player won't change. I don't think it's a big issue, but it would be nice.I'm having big trouble trying to make some nice collision resolution code :P...
EDIT: New approach, again. Looking at the rigid->immovable code, it uses getZeroImpulse to calculate an impulse that will stop the rigid entering the surface. I'm going to use the same method to calculate an impulse that will stop the player entering the vehicle.
EDIT: Another no-go. Well, I guess it's a semi-go. I don't understand whan invWorldInertia is (or at least how it is calculated and what it represents). But obviously Players don't have such a complicated physics system, so it's a bit daft to try to use it. I'm just going to have to figure out my own impulse calculations.
#5
I've worked up some really simple code.
velProj represents the component of the relative velocity in the direction of the normal of collision (the normal of the surface on the vehicle, that is). This is multiplied by the player's mass, then applied as an impulse. Since Player::applyImpulse takes a vector, divides by mass, then adds the result to its velocity, this should simply result in an increase of the player's velocity equal to velProj.
However, this is giving me really stupid results, as well as cases where the collision is never resolved, resulting in an endless loop in Vehicle::resolveContact (I think that's the method. The one with the do-while-colliding). For the record, I plan to remove that loop, but only when the engine works fine with it in. Collisions should not rely on multiple passes to resolve themselves.
I think the problem comes not only from my code, but from the retarded collision mesh in the buggy model. I'm going to see if I can get my hands on a different vehicle model to test with. Part of the problem, I suspect, is that the buggy's collision mesh only covers the top half of the model. So when the player hits the mesh, the normal points into the ground. Or something. I really don't know :P
I'm considering just pulling out all the old resolveDisplacement code and using it in Rigid::resolveCollision.
EDIT: Also, I've noticed an oddity in Player::displaceObject. The displacement fails if the player's velocity is less than 0.001... huh? So tationary players cannot be displaced? Clever.
EDIT: Actually, it seems that the engine's definition of displacing an object is something completely different to what I had in mind... hmm...
Ah! I see the light.
07/27/2008 (8:29 am)
Progress?I've worked up some really simple code.
bool Rigid::resolveCollision(const Point3F& p, const Point3F &normal, ShapeBase* other)
{
atRest = false;
Point3F v,r;
getOriginVector(p,&r);
getVelocity(r,&v);
v = other->getVelocity() - v;
F32 n = mDot(v,normal);
if ( n < 0 )
return false;
//Project relative velocity onto the normal
Point3F velProj = n * normal;
//Multiply by mass to get impulse
velProj *= other->getMass();
//Apply impulse
other->applyImpulse(p,velProj);
return true;
}The idea is to use the relative velocity of the player relative to the rigid to do everything. First, we do a nominal check to see whether we're converging.velProj represents the component of the relative velocity in the direction of the normal of collision (the normal of the surface on the vehicle, that is). This is multiplied by the player's mass, then applied as an impulse. Since Player::applyImpulse takes a vector, divides by mass, then adds the result to its velocity, this should simply result in an increase of the player's velocity equal to velProj.
However, this is giving me really stupid results, as well as cases where the collision is never resolved, resulting in an endless loop in Vehicle::resolveContact (I think that's the method. The one with the do-while-colliding). For the record, I plan to remove that loop, but only when the engine works fine with it in. Collisions should not rely on multiple passes to resolve themselves.
I think the problem comes not only from my code, but from the retarded collision mesh in the buggy model. I'm going to see if I can get my hands on a different vehicle model to test with. Part of the problem, I suspect, is that the buggy's collision mesh only covers the top half of the model. So when the player hits the mesh, the normal points into the ground. Or something. I really don't know :P
I'm considering just pulling out all the old resolveDisplacement code and using it in Rigid::resolveCollision.
EDIT: Also, I've noticed an oddity in Player::displaceObject. The displacement fails if the player's velocity is less than 0.001... huh? So tationary players cannot be displaced? Clever.
EDIT: Actually, it seems that the engine's definition of displacing an object is something completely different to what I had in mind... hmm...
Ah! I see the light.
#6
I came up with a slightly different approach, I don't know if it will be useful or not, but here it goes.
Instead of relying on collisions, I'd procedurally create a trigger for vehicles that can be walked upon. While a shapebase object is within the trigger cube, it'd have the trigger owner as its "holder". While a shapebase object is having a "holder", it'd apply the transformations of the holder object to those of its own. There you could go for measuring change, and apply the holder object's data weighted depending on velocity, turn speed, etc... This requires mild changes to the source too, of course, but nothing really big.
Also, the trigger's dimensions would help position objects on the "deck". This approach would also let you easily have hangar bay like features.
This is all cool as long as the holder object rotates only around the z axis. For x and y you'd have to apply a different method probably.
07/28/2008 (1:29 am)
I was thinking about this, since I will need this later on as well..I came up with a slightly different approach, I don't know if it will be useful or not, but here it goes.
Instead of relying on collisions, I'd procedurally create a trigger for vehicles that can be walked upon. While a shapebase object is within the trigger cube, it'd have the trigger owner as its "holder". While a shapebase object is having a "holder", it'd apply the transformations of the holder object to those of its own. There you could go for measuring change, and apply the holder object's data weighted depending on velocity, turn speed, etc... This requires mild changes to the source too, of course, but nothing really big.
Also, the trigger's dimensions would help position objects on the "deck". This approach would also let you easily have hangar bay like features.
This is all cool as long as the holder object rotates only around the z axis. For x and y you'd have to apply a different method probably.
#7
I'm making progress, I *think*. I still want to get my hands on a vehicle with a sensible collision mesh :P. In my frustration, I just decided to reinstate the original resolveDisplacement method - it wasn't what I had envisaged, but at least it worked.
The problem was that sometimes resolveDisplacement doesn't actually get the player totally out of the way, especially when the line between the vehicle and player is close to perpendicular to the vehicle's velocity. So I implemented a method in shapeBase next to queueCollision that checks whether a collision with a given object is already queued.
Then in the do-while loop, there are three clauses. If we're colliding with a vehicle, do a rigid->rigid collision. If we're colliding with a ShapeBase and we haven't already collided with it, do a rigid->immovable collision. In any other case, do a rigid->immovable.
Now when the player stands on the buggy, he doesn't stop it from moving (he just slides off, due to some wierd stuff going on in resolveDisplacement and the vehicle's collision mesh).
Though I'm worrying whether resolveDisplacement works for more than one object at a time. If not, that's a serious hole in my theory. The idea is to have a bunch of characters standing on a vehicle.
07/28/2008 (7:57 am)
It's sort of like the 'attachment' methods I was talking about earlier. No reason why it wouldn't work; I just prefer to sort it out through physics. If we're both going to implement this and we're doing it two different ways, I'm sorry we can't work together ;PQuote:Also, the trigger's dimensions would help position objects on the "deck". This approach would also let you easily have hangar bay like features.I'm having trouble envisaging how the whole thing works. Do objects collide with the trigger? Or do they just use it so they know when they need to move with a vehicle?
I'm making progress, I *think*. I still want to get my hands on a vehicle with a sensible collision mesh :P. In my frustration, I just decided to reinstate the original resolveDisplacement method - it wasn't what I had envisaged, but at least it worked.
The problem was that sometimes resolveDisplacement doesn't actually get the player totally out of the way, especially when the line between the vehicle and player is close to perpendicular to the vehicle's velocity. So I implemented a method in shapeBase next to queueCollision that checks whether a collision with a given object is already queued.
Then in the do-while loop, there are three clauses. If we're colliding with a vehicle, do a rigid->rigid collision. If we're colliding with a ShapeBase and we haven't already collided with it, do a rigid->immovable collision. In any other case, do a rigid->immovable.
Now when the player stands on the buggy, he doesn't stop it from moving (he just slides off, due to some wierd stuff going on in resolveDisplacement and the vehicle's collision mesh).
Though I'm worrying whether resolveDisplacement works for more than one object at a time. If not, that's a serious hole in my theory. The idea is to have a bunch of characters standing on a vehicle.
#8
Give the player a sort of interialResistance variable (a mix between their own balance and friction of their feet), that is how much faster an object under the player can be moving and still have the player come to rest on it. Example: You are standing on the roof of a car and it drives forward at 5mph, you are able to keep on it with a little effort. Now if that car were to zoom off at 40mph, you would be flipped right off.
In the Player::findContact for the player something like:
07/28/2008 (9:45 am)
Ok, just some thoughts about this:Give the player a sort of interialResistance variable (a mix between their own balance and friction of their feet), that is how much faster an object under the player can be moving and still have the player come to rest on it. Example: You are standing on the roof of a car and it drives forward at 5mph, you are able to keep on it with a little effort. Now if that car were to zoom off at 40mph, you would be flipped right off.
In the Player::findContact for the player something like:
Point3F contactSpeed = pConvex->getObject()->getVelocity();
if (contactSpeed - mVelocity <= mIntertialResistance)
{
mVelocity += contactSpeed - mVelocity;
//more stuff here?
}This is just a quick approach and doesn't handle anything like rotation, or actually setting the player at rest. Another quick idea is in the vehicles resolveCollision check if the colliding object is a player travelling at a speed difference less than his inertialResistance, and at a contactNormal above a certain degree (so if we ram them very slowly, it is still ramming and not standing on the bumper) and not damaging/pushing the player.
#9
The problem, as I see it, is the '- mVelocity'. When my player jumps onto a vehicle, I get a lot of jittering back and forth. I think this is due to the fact that rigid shapes always seem to have a little velocity (at least, the wheeled vehicle rigid does :P), so the player is always getting bumped around.
I *think* this is the problem - I'm going to start testing my code on a better computer, instead of the crapy laptop :P. Should allow me to make a lot more progress.
I thought of separating the surface velocity from mVelocity, and only using the new mContactVelocity in updatePos. Just a random idea.
Anyway - time to do more testing.
07/28/2008 (11:09 am)
Your change in Player::findContact is basically what I did, except I stuffed all the velocity inputs into a new parameter for that function, and actually used the vector in updateMove.The problem, as I see it, is the '- mVelocity'. When my player jumps onto a vehicle, I get a lot of jittering back and forth. I think this is due to the fact that rigid shapes always seem to have a little velocity (at least, the wheeled vehicle rigid does :P), so the player is always getting bumped around.
I *think* this is the problem - I'm going to start testing my code on a better computer, instead of the crapy laptop :P. Should allow me to make a lot more progress.
I thought of separating the surface velocity from mVelocity, and only using the new mContactVelocity in updatePos. Just a random idea.
Anyway - time to do more testing.
#10
07/28/2008 (12:03 pm)
Ah, well you could use something like an epsilon. Say if the difference between vehicle speed and player speed is less than 0.1, have no effect. This should stop the player from jittering whenever the vehicle makes even the slightests acceleration or deceleration.
#11
Another issue with contact velocity - the player seems to recieve two contacts from the same vehicle for some reason. I'm guessing this is because they touch in two places? Either way, he's getting twice the velocity he should from contactVelocity.
07/28/2008 (10:24 pm)
Yeah, I guess that's what I need - I did something like that, but my values were way too small (something like 0.001). ).1 seems like a good number, actually. I keep getting worried that if my tolerance is too high, collisions between the player and the vehicle won't be resolved - but they will, because that's done in separate code :P.Another issue with contact velocity - the player seems to recieve two contacts from the same vehicle for some reason. I'm guessing this is because they touch in two places? Either way, he's getting twice the velocity he should from contactVelocity.
Torque Owner Daniel Buckmaster
T3D Steering Committee
EDIT: resolveDisplacement is dead again. I replaced it with an overload of Rigid::resolveSomething that takes a ShapeBase* as an argument, so these collisions are sorted out with all the others. However, I still get the same problem.
The problem is that if the vehicle is starting from a standstill, its velocity is tiny, so no impulse is passed to the player standing on it. This doesn't seem to be realistic, because the vehicle is applying forces to its wheels, which should push both it and the player forward.
I found that in updateForces, vehicles tend to clear out their working data (mRigid.force and torque). I changed this so the data is cleared at the start of the function instead, so I now have access to force values for the rigid shape. However, I don't know whether it's a good idea to simply apply those forces to colliding objects.