Maarten Baert's website

Game Maker / C++ projects

Home Model Creator ExtremePhysics Game Maker DLLs SimpleScreenRecorder AlterPCB Quadcopters   Recent comments Search

Last modified: Sat, 22 Oct 2011
Refresh

World settings

It's very important to use suitable world settings. In this tutorial I will explain how to select suitable values for these settings.

ep_world_set_settings(world_id, timestep, velocity_iterations, position_iterations, contact_threshold, velocity_threshold, baumgarte_factor, mass_bias, position_factor)
timestep

This setting is used to change the length of the simulated time step. This setting doesn't actually change anything, but it makes it a lot easier to write code that doesn't depend on the room speed. ExtremePhysics will multiply all velocities and forces by timestep so you don't have to worry about the room speed anymore. This means you can change the room speed (i.e. the number of times ep_world_simulate_step is called) without adjusting all forces and velocities:

ep_world_set_settings(global.world, 1/room_speed, ...);
Warning

Changing the time step while the simulation is running is possible but not recommended. This will awake all bodies and possibly destabilize some constraints. Try to avoid changing the time step every step. Do not try to set the time step to 1/fps. This can cause tunneling (bodies moving through other bodies instead of colliding) when the fps is very low.

velocity_iterations

ExtremePhysics uses an iterative method to solve velocity constraints. velocity_iterations is used to change the number of performed iterations. Using a higher number of iterations will increase stability but will decrease performance. If the simulation is still unstable while you are using more than 50 iterations already, you should probably decrease the time step and call ep_world_simulate_step multiple times per step.

position_iterations

ExtremePhysics can optionally use a special position solver. This will usually give better results than Baumgarte stabilization alone, but it uses more processing time. Running a position iteration takes about three times as long as running a velocity iteration. You can set this to 0 to disable the position solver.

contact_threshold

This setting is used to reduce the number of contacts that are created and destroyed. Contacts are created when the separation between two shapes becomes smaller than -contact_threshold, and destroyed when the separation between two shapes becomes larger than contact_threshold. Set this to a value that is numerically significant but not noticeable, e.g. 0.1 (pixels).

velocity_threshold

Because of the way ExtremePhysics works, sometimes energy is added to the world when bodies collide, especially when using gravity (or other forces). This means bodies will sometimes keep bouncing forever, even if the coefficient of restitution is lower than 1. You can prevent this with this setting. You should set this to a value greater than the value of gravity. When two bodies collide, ExtremePhysics will subtract this value from the relative velocity of the bodies when calculating restitution. If the relative velocity of the bodies is lower than velocity_threshold, the collision is inelastic.

baumgarte_factor

The velocity solver ExtremePhysics uses is not perfect. After some time the velocity error can accumulate leading to unrealistic results. ExtremePhysics uses a method called Baumgarte stabilization to avoid this. The baumgarte factor changes the 'aggressiveness' of the algorithm. Setting this to a higher value will decrease the position error, but the simulation can become unstable if the value is too high. Most of the time 0.1 is fine.

Hint

Baumgarte stabilization sometimes interferes with the position solver. The bodies will sometimes keep moving very slowly instead of coming to rest. If this happens, disable either Baumgarte stabilization (set baumgarte_factor to 0, recommended) or the position solver (set position_iterations to 0).

mass_bias

This setting can be used to improve the speed of convergence of the velocity solver. You should set this to a value between 0 and 1. Usually 0.5 gives the best results.

position_factor

The position solver can become unstable in some situations. If this happens, you can usually fix this by decreasing the position factor. The default value is 1.


Comments

Rpgillespie

Comment #1: Sun, 8 Jan 2012, 9:56 (GMT+1, DST)

Quote


I am trying to set up time manipulation with your physics engine, but I'm having a tough time.

First, I tried putting this in the step function of the controller:
ep_world_set_settings(global.world, global.time, ...);
where global.time is a variable that I can control and is <= 1.

This worked great, except that I couldn't selectively choose which objects I wanted to be affected by time manipulation - it was either all or none.

Then I tried the more complicated approach of putting this in my step function of my controller

if (global.time < 1)
with (obj_crate) event_user(1);

ep_world_update_contacts(global.world);
ep_world_simulate_step(global.world);

if (global.time < 1)
with (obj_crate) event_user(0);

where event_user(1) sets the velocities of each crate to global.time * [each velocity]
and event_user(0) sets the velocities back to the way they were before event_user(1)

This works alright - the results aren't quite as good as my first approach, but at least I can control which objects are affected by time manipulation.

Do you have any suggestions on how I could selectively control time on objects and get results as good as my first approach?

Last modified: Sun, 8 Jan 2012, 9:59 (GMT+1, DST)

Rpgillespie

Comment #2: Mon, 9 Jan 2012, 1:08 (GMT+1, DST)

Quote


Nevermind, I figured out how. I was on the right track with my second approach, but I figured out I needed to set gravity to the square of global.time, among a few things that I had wrong.

Here's how I did it, if you are interested:

controller step:
if (global.time < 1)
with (obj_crate) event_user(1);

ep_world_update_contacts(global.world);
ep_world_simulate_step(global.world);

if (global.time < 1)
with (obj_crate) event_user(0);

obj_crate event_user(1):

ep_body_set_gravity(global.world,body,0,platformgravity*global.time*global.time);

xvel = ep_body_get_xvel_center(global.world, body);
yvel = ep_body_get_yvel_center(global.world, body);
rotvel = ep_body_get_rotvel(global.world,body);

dx = xvel - global.time*xvel;
dy = yvel - global.time*yvel;
dr = rotvel - global.time*rotvel;

ep_body_set_velocity_center(global.world, body, xvel*global.time, yvel*global.time, rotvel*global.time);

obj_crate event_user(0):

ep_body_set_gravity(global.world,body,0,platformgravity);

xvel = ep_body_get_xvel_center(global.world, body);
yvel = ep_body_get_yvel_center(global.world, body);
rotvel = ep_body_get_rotvel(global.world,body);

ep_body_set_velocity_center(global.world, body, xvel + dx, yvel + dy, rotvel + dr);

Maarten Baert

Administrator

Comment #3: Tue, 10 Jan 2012, 2:08 (GMT+1, DST)

Quote


@Rpgillespie: It's almost correct. You're right, gravity should be multiplied by global.time squared. But you should also ...
- Divide the mass by global.time, so the momentum (velocity times mass) doesn't change. Same for the moment of inertia.
- Divide the velocity by global.time after you've simulated a step, instead of adding what you subtracted the previous step.

obj_crate event_user(1):
ep_body_set_gravity(global.world,body,0,platformgravity*global.time*global.time);

mass = ep_body_get_mass(global.world, body);
inertia = ep_body_get_inertia(global.world, body);
xvel = ep_body_get_xvel_center(global.world, body);
yvel = ep_body_get_yvel_center(global.world, body);
rotvel = ep_body_get_rotvel(global.world,body);

ep_body_set_mass(global.world, body, mass/global.time);
ep_body_set_inertia(global.world, body, inertia/global.time);
ep_body_set_velocity_center(global.world, body, xvel*global.time, yvel*global.time, rotvel*global.time);
obj_crate event_user(0):
ep_body_set_gravity(global.world,body,0,platformgravity);

mass = ep_body_get_mass(global.world, body);
inertia = ep_body_get_inertia(global.world, body);
xvel = ep_body_get_xvel_center(global.world, body);
yvel = ep_body_get_yvel_center(global.world, body);
rotvel = ep_body_get_rotvel(global.world,body);

ep_body_set_mass(global.world, body, mass*global.time);
ep_body_set_inertia(global.world, body, inertia*global.time);
ep_body_set_velocity_center(global.world, body, xvel/global.time, yvel/global.time, rotvel/global.time);

I might have missed something, but I think this is everything. Just make sure that global.time isn't zero (or you will get divide-by-zero errors). If you want to be able to freeze time, you basically have to change the velocity to 0 and the mass to infinite. That means the bodies should be made static. You also have to remove all joints between frozen bodies temporarily. As you might have guessed this isn't exactly easy :). This is actually what I did in my game The Machine.

Last modified: Tue, 10 Jan 2012, 2:09 (GMT+1, DST)

Rpgillespie

Comment #4: Tue, 10 Jan 2012, 4:43 (GMT+1, DST)

Quote


Quote: Maarten Baert

@Rpgillespie: It's almost correct. You're right, gravity should be multiplied by global.time squared. But you should also ...
- Divide the mass by global.time, so the momentum (velocity times mass) doesn't change. Same for the moment of inertia.
- Divide the velocity by global.time after you've simulated a step, instead of adding what you subtracted the previous step

...

I might have missed something, but I think this is everything. Just make sure that global.time isn't zero (or you will get divide-by-zero errors). If you want to be able to freeze time, you basically have to change the velocity to 0 and the mass to infinite. That means the bodies should be made static. You also have to remove all joints between frozen bodies temporarily. As you might have guessed this isn't exactly easy :). This is actually what I did in my game The Machine.

I do want to be able to freeze time. Right now I just have it setting the velocity to 0, so they appear frozen, but they can actually still be moved if you jump on them, etc., since they are not static.

When you say "That means the bodies should be made static," is there a way to do this other than deleting the current body, creating a new static one with the same orientation, and then doing the reverse as soon as time is unfrozen? There's no function that will simply switch a body to static, right?

Also, why do you have to remove all joints between frozen bodies?

Rpgillespie

Comment #5: Tue, 10 Jan 2012, 4:50 (GMT+1, DST)

Quote


Quote: Maarten Baert

Just make sure that global.time isn't zero (or you will get divide-by-zero errors). If you want to be able to freeze time, you basically have to change the velocity to 0 and the mass to infinite.

For a quick fix, I just set the mass and inertia to an extremely high value (i.e. 99999999), and it seems to work pretty darn well. Is there any downside to doing this as opposed to making it static? I suppose if the force was great enough the bodies could still be moved, but it's highly unlikely. Here's what I have:

obj_controller step event:

if (global.time < 1)
with (obj_time) event_user(0);     //anything that inherits obj_time will be affected by time

ep_world_update_contacts(global.world);
ep_world_simulate_step(global.world);
    
if (global.time < 1)
with (obj_time) event_user(1);

obj_time user_event(0):

ep_body_set_gravity(global.world,body,0,platformgravity*global.time*global.time);

mass = ep_body_get_mass(global.world, body);
inertia = ep_body_get_inertia(global.world, body);
xvel = ep_body_get_xvel_center(global.world, body);
yvel = ep_body_get_yvel_center(global.world, body);
rotvel = ep_body_get_rotvel(global.world,body);

if (global.time != 0)
{
    if (timefrozen)
    {
        timefrozen = false;
        load_state();    
    }

    ep_body_set_mass(global.world, body, mass/global.time);
    ep_body_set_inertia(global.world, body, inertia/global.time);
    ep_body_set_velocity_center(global.world, body, xvel*global.time, yvel*global.time, rotvel*global.time);
}
else
{
    if (!timefrozen)
    {
        timefrozen = true;
        save_state();    
    }

    ep_body_set_mass(global.world, body, 999999999);
    ep_body_set_inertia(global.world, body, 999999999);
    ep_body_set_velocity_center(global.world, body, 0, 0, 0);
}

obj_time user_event(1):

if (global.time != 0)
{
    ep_body_set_gravity(global.world,body,0,platformgravity);

    mass = ep_body_get_mass(global.world, body);
    inertia = ep_body_get_inertia(global.world, body);
    xvel = ep_body_get_xvel_center(global.world, body);
    yvel = ep_body_get_yvel_center(global.world, body);
    rotvel = ep_body_get_rotvel(global.world,body);

    ep_body_set_mass(global.world, body, mass*global.time);
    ep_body_set_inertia(global.world, body, inertia*global.time);
    ep_body_set_velocity_center(global.world, body, xvel/global.time, yvel/global.time, rotvel/global.time);
}

Last modified: Tue, 10 Jan 2012, 5:22 (GMT+1, DST)

Maarten Baert

Administrator

Comment #6: Fri, 20 Jan 2012, 0:58 (GMT+1, DST)

Quote


Quote: Rpgillespie

Also, why do you have to remove all joints between frozen bodies?

Because the force needed to move the bodies would be infinite. Essentially it results in a divide by zero which would crash the game. So ExtremePhysics just doesn't allow it (you will get an error message).

Quote: Rpgillespie

When you say "That means the bodies should be made static," is there a way to do this other than deleting the current body, creating a new static one with the same orientation, and then doing the reverse as soon as time is unfrozen? There's no function that will simply switch a body to static, right?

Currently, no. Deleting and recreating is exactly what I did in The Machine.

In one of the first versions of ExtremePhysics you could actually change the 'static-ness' of bodies, but as a result I had to do a lot of checks (things like checking whether both bodies of a joint are static). In those cases the engine would show an error message every iteration which was pretty annoying. It was also a bit of a waste of processing time because I had to do this check every iteration for every joint, even the ones that were fine. But I guess that wouldn't be an issue anymore in 2.2 because I'm already ignoring joints of sleeping bodies (in a relatively efficient way). I could allow joints between static bodies and simply ignore them. I'm not sure how changing the 'static-ness' would interfere with the sleeping system though (the sleeping system handles static bodies completely differently). It's definitely possible but not that easy. Maybe I will do it in 2.3, it's something people ask a lot.

Quote: Rpgillespie

For a quick fix, I just set the mass and inertia to an extremely high value (i.e. 99999999), and it seems to work pretty darn well. Is there any downside to doing this as opposed to making it static? I suppose if the force was great enough the bodies could still be moved, but it's highly unlikely.

The bodies will still move, but not very much. If it's not good enough, you can always increase the mass. I don't think there's a limit, something like 10^100 should still work. In fact you can set the mass and inertia high enough so the actual velocity becomes too low to actually change the position (because of rounding errors). The position is saved with about 16 significant digits, so if the velocity is more than about 10^16 times smaller than the position, it won't move at all.

Monstr

Comment #7: Tue, 26 Jun 2012, 18:00 (GMT+1, DST)

Quote


I'm not sure about this timestep thing. So do I need to have ep_world_simulate_step in the end step section or not? :/

Maarten Baert

Administrator

Comment #8: Fri, 6 Jul 2012, 19:56 (GMT+1, DST)

Quote


Quote: Monstr

I'm not sure about this timestep thing. So do I need to have ep_world_simulate_step in the end step section or not? :/

You have to call it every step, yes. It could be in the end step event, but the step event is fine too. It depends a bit on what you're trying to do.

Monstr

Comment #9: Thu, 12 Jul 2012, 19:06 (GMT+1, DST)

Quote


Quote: Maarten Baert
Quote: Monstr

I'm not sure about this timestep thing. So do I need to have ep_world_simulate_step in the end step section or not? :/

You have to call it every step, yes. It could be in the end step event, but the step event is fine too. It depends a bit on what you're trying to do.

I want to have a game where you can change the max fps, depending on the quality of your PC. Of course I want the game to run at the same speed.
What I have right now is:

ep_world_settings(.../*timestep*/global.setfps/60)

The game was programmed for 60 fps. I have kept the advance step every end step. Everything speeds up except the player.

Possibly it is because the velocity of the player is set by button presses wheras most objects aren't. If you want code for player it is based almost completely on the tutorial platformer.

Maarten Baert

Administrator

Comment #10: Sun, 12 Aug 2012, 20:33 (GMT+1, DST)

Quote


Quote: Monstr

I want to have a game where you can change the max fps, depending on the quality of your PC. Of course I want the game to run at the same speed.
What I have right now is:

ep_world_settings(.../*timestep*/global.setfps/60)

The game was programmed for 60 fps. I have kept the advance step every end step. Everything speeds up except the player.

Possibly it is because the velocity of the player is set by button presses wheras most objects aren't. If you want code for player it is based almost completely on the tutorial platformer.

It should be 60/global.setfps, not global.setfps/60 :).

Zerozero11

Comment #11: Sat, 5 Jan 2013, 20:54 (GMT+1, DST)

Quote


Hi Maarten.

I installed ExtremePhysics yesterday so I'm still getting used to the code. I'm trying to implement your engine in this game I've already half-made to allow my units to bump into each other realistically. I want to be able to keep most of the code I wrote for changing angular velocities, linear velocities, etc:

what is the time step I should use? I've tried 1/room_speed and I had to increase the numbers for linear movement by over 100x to keep my speeds the way they should be, though angular velocity remains normal.

I've used in the step event

Quote

ep_body_set_velocity_center(global.world,body,hspeed,vspeed,angvel);

And in the end step event

Quote

x = ep_body_get_x(global.world,body);
y = ep_body_get_y(global.world,body);
dir = radtodeg(ep_body_get_rot(global.world,body));

Thanks in advance!

Last modified: Sat, 5 Jan 2013, 20:54 (GMT+1, DST)

Maarten Baert

Administrator

Comment #12: Sat, 5 Jan 2013, 22:29 (GMT+1, DST)

Quote


Quote: Zerozero11

what is the time step I should use? I've tried 1/room_speed and I had to increase the numbers for linear movement by over 100x to keep my speeds the way they should be, though angular velocity remains normal.

If you were previously using hspeed and vspeed, you probably want to set time step to 1. That way you can keep all your existing speeds.

Quote: Zerozero11

I've used in the step event

ep_body_set_velocity_center(global.world,body,hspeed,vspeed,angvel);

It's usually better to avoid hspeed and vspeed when you are also using EP for that object, because the two can interfere depending on how you use them. This is why I use xspeed and yspeed. It may not be a problem in this case though.

Zerozero11

Comment #13: Sat, 5 Jan 2013, 23:04 (GMT+1, DST)

Quote


Quote: Maarten Baert
Quote: Zerozero11

I've used in the step event

ep_body_set_velocity_center(global.world,body,hspeed,vspeed,angvel);

It's usually better to avoid hspeed and vspeed when you are also using EP for that object, because the two can interfere depending on how you use them. This is why I use xspeed and yspeed. It may not be a problem in this case though.

Thanks Maarten! Now the speeds work perfectly.

Can you tell me in what sorts of situations they will interfere?

Maarten Baert

Administrator

Comment #14: Sun, 6 Jan 2013, 0:30 (GMT+1, DST)

Quote


Quote: Zerozero11

Thanks Maarten! Now the speeds work perfectly.

Can you tell me in what sorts of situations they will interfere?

They can interfere if your code is not executed in the correct order. As long as you only change hspeed/vspeed in the step event, collision events etc. and not in the end step event or draw event, it should be fine.

The reason it doesn't interfere is that GM changes x and y right before the end step event, and EP overrides these values in the end step event. If you were using x and y in between, the values would be wrong.

Lukayson

Comment #15: Fri, 19 Jun 2015, 0:59 (GMT+1, DST)

Quote


Hello, I need to ask, does Extreme Physics work with random values inside ? I need same behavior all the time i cutscene. Thanks

Maarten Baert

Administrator

Comment #16: Sun, 28 Jun 2015, 11:02 (GMT+1, DST)

Quote


Quote: Lukayson

Hello, I need to ask, does Extreme Physics work with random values inside ? I need same behavior all the time i cutscene. Thanks

ExtremePhysics is deterministic. If you run the same simulation twice, you get exactly the same result. This only works when the entire simulation is exactly the same.

Write a comment