Issues with friction, collision, etc.

Topics: User Forum
Dec 2, 2009 at 12:33 PM

Hello,

First off, thanks for a great engine. So simple to use, yet so powerful.

Okay, so I have a few questions, but I'll give a little background on what I'm doing.

I'm writing a top-down game which has different vehicles (such as tanks, cars, whatever) and other objects (for now just crates). I'm doing this in C# using XNA and Farseer. I have a early university understanding of physics, most of which I have forgotten. Some facts about the game:

  • Each game object has an attached body and geom which sizes to the game object's sprite (scaled to sim units).
  • I am working with metres/sec as the sim units (so, a 50km/h tank will move at around 14m/s - a velocity of 14.0f).
  • I have similarly set the "unit to sim" ratio to a value representative of the graphics I'm using (a tank is 50 pixels long which is about 7m for example). I have set the DisplayUnitToSimUnitRatio to 7.14 = 50px/7m.

Phew. Okay. Here are my questions:

  1. To move the tank, I am using ApplyForceAtLocalPoint (at the origin of the body) with a force of F=ma (where Fx = mass * accel * cos(rotation), Fy = mass * accel * sin(rotation), rotation is the angle that the body is facing, and accel is in m/s^2). This occurs every dt if the player is holding down the accelerate key, and not at all if the player isn't holding down the key. Does this sound correct?
  2. To rotate the tank, I apply an acceleration value to body.AngularVelocity each update. That is, body.AngularVelocity -= rotation_accel * seconds. When the user presses left or right, it sets the acceleration to +/- some value. Does this sound correct too?
  3. To slow the tank down, I use body.LinearDragCoefficient since there are no objects colliding with it to cause it to slow down (as in, I can't use geom.FrictionCoefficient, right?). I don't know exactly what this value means, so I've experimented and set it to 20.0f and that seems to work "alright". Is this correct usage of the drag coefficient? What does it mean?
  4. To slow the rotation of the tank down, I manually apply a drag value as an acceleration value each game update. That is, AngularVelocity += drag * dt. I couldn't really get RotationalDragCoefficient working properly - how do I use it?
  5. I have my mass values set to arbitrary (but consistent) values. For example, the tank weighs "14.0f". Should this be a relative value to distance, or can it be completely separate (but still consistent)?
  6. I have a turret on the tank, and it's "ignoring collisions" with the tank. I have the turret point towards the mouse cursor (the body rotation is updated each dt). Does this sound like a reasonable approach?
  7. The turret shoots bullets (which are tiny bodies themselves with a small mass). When the bullets hit a crate (for example) the crate jitters around. Why is this?

Sorry if that's a bit long-winded - not good at explaining myself clearly!

Thanks heaps if you manage to decipher it and take the time to reply.

Coordinator
Dec 2, 2009 at 3:28 PM
Edited Dec 2, 2009 at 3:29 PM

1. We have a great foundation for a tank inside our AdvancedSamples. You move the tank using force, but since we are a physics engine, you can actually simulate the engine and track of a real tank. Take a look at it and see if it tickles your imagination.

Otherwise you can use ApplyForce() to apply force in a direction. It applies the force at the centroid of the geometry (the body position) - the amount you apply is really up to you. You just have to find a value range that "feels" right to you. As for the accelerate key - Force is the correct thing to use here since it has to be constantly applied to make an object move.

2. Use ApplyTorque() instead.

3. If you use ApplyForce() and ApplyTorque, you can use the FrictionCoefficient property. The LinearDragCoefficient is the amount of drag applied to the object at each update.

It is calculated like this: LinearDrag = -LinearVelocity * LinearDragCoefficient
and then the LinearDrag amount is applied to the body as a force.

4. Use RotationalDragCoefficient. It works, you just have to apply a higher value.

5. The mass is used to determine the amount of force that should be applied in reactions and how much force it is required to move an object. As you said yourself: F = ma. A higher mass makes the tank move a shorter distance with the same force.

6. Yes. But it depends on what kind of reaction you want to have. If your tank is hit by a missile, do you want the turret to detect collisions separate from the tank? You can combine multiple geometries using one body. This is by far the easiest in most cases, but if you need the turret to be a separate unit for some reason, you need to create two geometries, and ignore collision between the two.

7. How does it jitter around? The bullet might be stuck in the create (due to tunneling) and that could cause the crate to jump around. Once a bullet has hit something, it might be a good idea to remove it from the physics simulator (if your tank can shoot a lot of bullets in a short period of time, make sure you use a pool).

 

Dec 3, 2009 at 3:28 AM

Thanks for the quick reply and detailed information. I've worked through most of what you've suggested, and here's where I'm at..

  • The tank has a mass of 20.0f, a crate has a mass of 4.0f, and a projectile (tank shell) has a mass of 1.0f.
  • The tank and crate initially have a LinearDragCoefficient of 40.0f. I couldn't figure out how the geometry.FrictionCoefficient works - is it only for collisions? Would it be better for me to apply another force on the tank, which is the friction of the surface it is on? For example, if the tank is on asphalt, the frictional force is different to when the tank is on ice.
  • The projectile initially has a LinearDragCoefficient of 0.001f (to simulate wind resistance). When the projectile falls outside of its assigned range, or when it hits something, the LinearDragCoefficient is set to 40.0f (as above).
  • All objects have a RotationalDragCoefficient of 1000.0f.

Movement:

  • When the player has the accelerate key pressed, the tank has a constant force of mass * acceleration = 20.0f * 30.0f applied to it at the correct angle (as in my previous post). I've changed this to ApplyForce (from ApplyForceAtLocalPoint). When the key is released, I no longer call ApplyForce.

Rotation:

  • When the player has the "turn" key pressed, the tank has a constant torque applied to it. This torque value is +/-500.0f (depending on if it's left or right).

Shooting:

  • When the player shoots a projectile, its velocity is set to 230.0f at the angle of the turret.

So aside from the questions I've already posed above (and anything you think I need clarification on), here's what happens:

  • Driving around and bumping into crates and other tanks works as expected (as in, it looks "normal").
  • Shooting looks normal when no collision occurs - it slows down and stops when outside its defined range).
  • When a projectile collides with another object (whether a tank or a crate, or another projectile), I get the dreaded ArithmeticException with NaN value in AngularVelocity.

I'm not sure what to do from here-on-in. Any advice is most welcome. Unfortunately the tank example in the AdvancedSamples isn't terribly useful as it's "side-on" whereas my game is "top-down". It did, however, inspire me to perhaps work on a side-on tank game :)

Dec 3, 2009 at 6:54 AM
Edited Dec 3, 2009 at 7:50 AM

Actually, I've also had the NaN exception from just driving around. I have read around the forums about other people getting this, and it seems like a common suggestion is to scale down the values. I believe I have done this as best I can, so I'm not sure what else to do. If I put these lines (the "if" statement) into Body.cs just before the exception gets raised:

//Calculate rotational drag and apply it as torque
if (Math.Abs(AngularVelocity) > 2.0f)
  AngularVelocity = Math.Sign(AngularVelocity) * 2.0f;
_rotationalDrag = AngularVelocity * AngularVelocity * Math.Sign(AngularVelocity);
_rotationalDrag *= -RotationalDragCoefficient;
ApplyTorque(_rotationalDrag);

..then it mostly works. This seems like a bit of a hack though.

Have I missed something obvious?

Dec 7, 2009 at 2:15 PM

Just touching base - wondering if anyone has any suggestions with what I've mentioned above?