Getting physics right for Polars in UE4

Morva Kristóf
Polars Technical Blog
8 min readApr 24, 2019

--

Polars is a magnet-based indie puzzle game, relying heavily on physics. In this blog post we’ll describe the challenges we have encountered regarding physics and the solutions we have implemented to counteract them.

We didn’t really work with physics before (besides an Epic Jam entry of ours), so we lived under the naïve impression, that we’ll just set meshes to simulate physics, and it’ll work well just like that. Boy, were we wrong.

Framerate dependency

As default, physics in Unreal Engine depends on your framerate. It means, that simulation won’t behave the same way if you have 20 or 60 FPS. Now, if you have a shooter game where a grenade explosion will throw away some nearby plastic cans, it really doesn’t matter. However, in the case where physics actually affects gameplay, it’s a serious problem. Imagine a platformer game where you have meticulously calculated the optimal force you need to push a box with, just to realize on another computer, that the result is totally different with 22 frames per second.

Below you can see how physics can differ just by using a different framerate.

Expected physics behavior with 120 FPS
Incorrect, fastening physics with 44 FPS

Luckily, there is a solution for it in Unreal Engine: physics substepping. You can enable it in the project settings, alongside with settings to configure the goal rate you’d like your physics to be ticked in (usually the max FPS you’re shooting for). Turned on, this setting will split every frame to multiple substeps (to reach your target FPS), in which all the physics calculation will be done, independently from your game thread’s framerate. If you target 120 FPS, and you have at least 120 FPS, each frame will have only 1 substep. However, if you have only 45 FPS, each frame will have 3 physics substeps, so that it can reach the physics tickrate you’re aiming for.

If you just enable this setting, you’ll see that now if you spawn some physics bodies, they will act consistently with different FPS. Wohooo, that way easy! Or was it? The issue comes, when you want to continuously apply dynamic forces. As default, every substep will work with the force value you’ve set in the beginning of the frame, meaning, if you add a force of 100 in the game thread, all substeps will calculate with a force of 100. Why is it a problem? In Polars for example, we apply forces based on the distance. Let’s see a simplified example of adding a 0.1 * distance force to our actor in each frame:

  • If you have 40 FPS with a physics tickrate of 120, then you’ll have 3 substeps, resulting in distance + 3 * (0.1 * distance) in total at the end of the frame, simply because each substep will work with the same, original distance.
  • If you have 120 FPS however, and you use the same calculation, after 3 frames you’ll get distance + (0.1 * (distance + 0.1 * (distance + (0.1 * distance)))), because you have only one substep per frame, so each substep can work with the newly set distance.

To solve this, you’ll have to set up the physics tick of your component in C++ (OnCalculateCustomPhysics), where you can calculate physics with the result of the previous substep, having the same physics result as if you had 120 FPS, even if you had only 40.

There is a great overview and tutorial to set everything up here: https://avilapa.github.io/post/framerate-independent-physics-in-ue4/

And a detailed technical explanation of substepping here: http://www.aclockworkberry.com/unreal-engine-substepping/

Unwanted momentum

Using the momentum of an object for bouncing might be extremely important for some games, like soccer or basketball, however, they might not work well for your game. Specifically in Polars, one of our testers was able to headbutt the magnet out of a zone, which should be impossible purely with the forces working in that area.

After wasting some time looking for a proper solution with physics materials and numerous physics settings without any success, I’ve cheated it. Simply, when a physics object has Hit the player’s capsule component, I have applied some inverse force to slow it down.

I know, I know, I’d also spit myself in the face when I use solutions like this, but until I find a better one, it’s gonna work.

Pushing away the based object

It’s another disgusting issue. Sometimes, walking off of a physics body causes the object to be launched into the sun, even if physics interaction has been completely turned off.

Shooting box away by climbing down the edge

In the example above it’s not thrown away so much, but sometimes just by normally walking off, the box jumps to the other side of the room. After a day of struggling I have found a reddit post with my exact problem, including a solution: checking Use Flat Base for Floor Checks in the CharacterMovementComponent has actually got rid of the issue. However, it has resulted in the smoothness of ledge climbing to totally disappear, jumping up on a ledge made the player teleport onto the surface. It could work perfectly for a 2D platformer, but not in an FPS for sure. After a few days of nightmares, just from pure rage I set everything to random values in the whole movement component, and seen that the issue has magically disappeared. Then I started resetting properties back to their default value, until I have identified the culprit: MaxStepHeight. It is 45 as default, and since I set it to 4, it’s working wonderfully. I believe the origin of the problem could be, that since the capsule component is elliptic on the bottom, the player’s distance from the ground will change while we are walking down from an object, and as soon as this distance reaches MaxStepHeight, the character is put on the ground, forcing the object to be displaced.

Floating point inaccuracy

Unreal Engine is using 32-bit floating point numbers, meaning there are 32 bits available in total for all the sign, the significant and the exponent part. It applies to every single calculation, including all physics simulations too. While we are calculating transformations near the origin, the precision we can achieve will be sufficient for any kind of transformation, however, as you start transforming things further away, you will see that you lose some accuracy along the way, resulting in erroneous or unexpected behaviors. The reason for this is, that floats are more dense near 0, and start to be less-and-less accurate as you go further.

Distribution of float values (image from volkerschatz.com)

It introduces issues for any large-world game, like PUBG or Fortnite. The solution (besides using 64-bit floats), that is also used by PUBG, is world origin rebasing (or world origin shifting). All it does is, that it offsets the whole level (every single actor) with a vector, so that the coordinate (0, 0, 0) will appear near the spot where most of the calculations need to happen, which is usually around the player.

Polars is built up with sequential levels. Each time the player reaches the end of a cell, we close the door behind him, load the new cell, and open the door leading forward. It’s done really fast, and minimizes the number of actors present in the level at any given time. However, we started to experience float inaccuracy issues, when after a few completed cells the engine couldn’t apply lightmaps properly, as they were off by some really minimal amount (like 0.001 cm). Even if we could have overcome this issue, I was afraid that if a level contains many big cells, even our physics can become unreliable, which is not something we can afford in a game, which is all built around physics.

To solve it, we also applied a world origin shift at the end of each cell in a very basic way: we just moved the whole transition streaming level to the origin. At this point even in the final game we plan to have less than 50 actors on the whole level, so we should easily get it done in a single frame.

We encountered two little issues. The first one is more straightforward: as we moved the player, we had one frame of motion blur, so we just hacked it too: we disabled the motion blur only for a single frame.

Turning off post process for a frame

Update: I have found that PlayerCameraManager has a bCameraCutThisFrame property, which, if set to true, will take care of motion blur for one frame. It is most likely preferred over manually tuning the post process struct.

The other issue needed a bit more time to solve, but as soon as one figures out what the hell is happening (and where in the source code), it’s easy to overcome. While the player was standing on the floor, the origin shifting automatically took the player character to the new destination, however, if at this moment the player was in the air (i.e. jumping), they were not traversed automatically. It resulted in the issue, that even if we are shifting the player location or not, there was a case where they landed out in nowhere. It took some time to find what is actually moving the player, which was the movement component’s UpdateBasedMovement method. If the component detects that the character is standing on an actor, and that actor is moved, then it also offsets the character itself. It’s great of course if the player is standing on a conveyor belt or a moving car, but really unlucky if you want to move both the player and the environment to a different location, when the player may or may not be in the air. After locating this behavior, the fix is of course trivial: override the function, and if we are doing origin rebasing, don’t call the parent method via Super.

void UPlayerMovementComponent::UpdateBasedMovement(float DeltaSeconds)
{
if (bUseBasedMovement) {
Super::UpdateBasedMovement(DeltaSeconds);
}
}

Although I’m not exactly sure that I have always found the best solution for the problems above, I hope some of these insights will be able to help you out. If you have a better idea for any of these issues, we’d be glad if you’d get in touch with us to share your thoughts!

--

--

Morva Kristóf
Polars Technical Blog

Game Developer, lover of open source projects, freedom, sustainability and strawberries.