Question

Basically I'm working out drag in a game with physics. The sideways motion needs to be reduced (as in brought closer to 0, not just brought into negative), but I don't know which direction it's travelling in, so I don't know whether to add drag or subtract drag.

(If an item is moving right, it has positive force, moving left, negative force)

Right now I'm doing this:

if (objects[i]->force.x > 0)
    objects[i]->force.x -= drag;
else
    objects[i]->force.x += drag;

Which works fine, but I get the feeling that there's bound to be a sleeker way to do this.

It also couldnt hurt to find a way to ensure it doesn't go past 0

Était-ce utile?

La solution

If you are trying to emulate friction, the easiest way, is to know the velocity and use a mathematical model, or differential equation to approximate it.

     dx
-c * --
     dt

basically, the force of friction is proportional to the velocity and in opposite direction. Thus, you can calculate the force of friction (or drag) simply as follows:

objects[i]->force = -drag_coefficient * objects[i]->velocity

If you do not know the velocity, it gets difficult to get good results, as you may have a force pushing right, while the object is moving left (therefore you would apply drag to the wrong direction).

Edit:

Actually, not all kinds of friction depend on the magnitude (speed) of the velocity, dry friction is often referred as just taking the direction of the velocity into account:

force = -drag_coefficient * sign(velocity)

However computationally this becomes unstable as velocity reaches 0, overshoots it and oscillates back and forth.

Another model is the one for wet friction, that is the one I used in the first example:

force = -drag_coefficient * velocity

This means that as your object is running faster, it will be slowed down more by friction. Think for example of the force of air, as you are running, the faster you are running, the more the air is trying to slow you down.

Neither of the models above is 100% accurate, but for a game they should be more than enough

Autres conseils

Drag should be a function of velocity: drag = r_1 * v where r_1 < 0. That would cause the drag force to be opposite velocity. Then you can just add the drag force.

First, we should clarify what is represented here. You write “If an item is moving right, it has positive force…” Usually, if an item is moving right, we would expect it to have a positive velocity in that direction, not necessarily a positive force.

If you are dragging a solid object on a solid surface, the “ideal” friction resisting the dragging motion is the force pushing the object into the surface (e.g., gravity) multiplied by the coefficient of friction (dependent on the materials involved). Dividing this force by the mass of the object produces an acceleration. In a situation where the object (including its mass), materials, and perpendicular force are not changing, you can calculate this acceleration once and treat it as a constant, a.

In a continuous model, this acceleration decreases the velocity to zero smoothly. In your model, you are probably using discrete time steps, some amount of time t. Thus, in each normal step, the velocity decreases by acceleration times the step time, a*t. We can also calculate that and assign it to some variable, say d.

A problem occurs because your discrete time step may overshoot the moment when velocity becomes zero. To deal with this, you might use code such as:

if (velocity > d)
    velocity -= d; // Resist rightward velocity for a full time step.
else if (velocity < -d)
    velocity += d; // Resist leftward velocity for a full time step.
else
    velocity = 0;  // Resist small velocity until it becomes zero.

Certainly one could use other models, and there are different physics for objects that are not solid. A simple model that some have mentioned in other answers and comments is diminishing velocity by multiplying by a value less than one. That example does not model the real-world physics of dragging a solid object. Whether it is suitable for your game may be an aesthetic choice. If you use it, your code might be simply:

velocity *= factor;

One problem with that is that some processors have very poor performance for subnormal numbers. The most common floating-point arithmetic used, IEEE-754, specifies an interval for very small numbers that are handled specially (to get certain mathematical properties that assist with proofs and predicting behavior). An expression such as velocity *= factor; will drive numbers to be smaller and smaller, in the absence of any other changes, until they reach this subnormal interval. On vulnerable processors, this will cause the performance of a program to drop suddenly. This may of course ruin game physics. Some systems set a processor mode in which subnormal numbers are converted to zero. This avoids the performance problem but violates the IEEE-754 standard. It may be acceptable for game physics.

If you're happy with your model (noting caveats in the comments), you can shorten the expression by using a signum function. Although in C++ choosing one is apparently somewhat involved....

Whichever one you choose, I'll refer to it here as sgn(). You then want the sign of the applied drag to be the opposite sign of force.x:

objects[i]->force.x += -1 * sgn(objects[i]->force.x) * drag;

(I know I could just use an unary minus here; I think this makes intent clearer)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top