Space Oddity

Greetings Everyone.

I have been following UE4 for awhile now, and finally found myself a hobby project that I can fill my spare time with.
Ever since UE4 became free I’ve been lurking on the forums, watching tutorials and streams and got really impressed with what community achieves in this wonderful engine.
After some project attempts I eventually decided to implement physics based spaceship flight model and then experiment with AI. Space is cool - not much stuff to model required before one can start experimenting with physics.
Anyway, I built myself a parametric star sphere skybox (first attempt with cubemaps was quick and easy but I felt that they are too heavy considering what they achieve). And started experiments with the UE4 PhysX physics model.

The initial research was quite frustrating and I had to dig through the forums, answer pages and just do plain trial-and-error in blueprints.

So I decided to give back to the community and document my findings that I feel will be useful for developers who are just starting similar research. Hopefully this will help you save some time.
And hopefully project updates will also follow :slight_smile:

RigidBody study - linear motion

First I created myself a simple blueprint with sphere (1m diameter) for testing and added UMG widget displaying debug info.


First problem: I did override the sphere mass setting to be 1kg but see how it is still reported as 64kg?


Solution: In order to override mass, you need to switch off MassInKg and add the following in construction script:


Now everything looks ok and we can start moving!


One can just use SetPhysicsLinearVelocity but this should be avoided and used only for kinematic objects without physics simulation (although it is a good node to stop the object when testing).
For linear motion Add Impulse or Add Force should be used.

Force: F=m*a SI units: N (Newton), kgm/(s^2)
Momentum: **p=m
v** SI units: kgm/s
Impulse: integral of the force, and can be expressed in simpler form as J=m(v2-v1) SI units: kg

Note that UE4 uses different units.
Force: kg*cm/(s^2)
Impulse: kg*cm/s

Adding impulse.
You can see this as an instantaneous force applied that causes velocity change of the object of constant mass from v1 to v2. To compute impulse value you need to multiply your object mass by desired velocity change. So in my test with 1kg sphere if I want to instantly accelerate it from standstill to 5m/s, I need to use impulse value of 500.


Adding force.
Unlike impulse, force needs to be applied over period of time. UE4 does a perfect job accounting for time between event ticks when Add Force node is used. When computing force value you need to multiply object mass by desired velocity change and divide by duration in seconds when force will be applied. In my test if I want to accelerate 1kg sphere from standstill to 5m/s over 2s, I need to use force value of 250.


As you can see, while gate was open, sphere accelerated at 2.5m/s^2 and when delay node closed the gate after 2s, sphere had velocity of exactly 5m/s.

RigidBody study - elastic collisions

After setting up two spheres and colliding them I quickly realized that collision was well… yeah… not really elastic.
Here comes another important part: create Physical Material and assign it to the component that has physics simulated.
Physics Material provides several important parameters that control behavior during physics simulation and I see no point in not creating and assigning one.
Parameter that will let us have elastic collisions is Restitution. UE4 hints it as surface bounciness, alternatively you can see it as a coefficient that controls energy percentage that get converted during collision to deformation, heat, sound, etc.
Setting it to 1 nets completely elastic collisions.

As you can see, UE4+physX do a pretty good job - perfect momentum and energy conservation. However, last 2 segments are curious - when 1kg sphere with 12.5J kinetic energy bounced off 1tonn sphere 0.05J of energy got lost. Final segment is even more extreme - both spheres got to a complete stop and total system energy became 0. The reason here I believe is that physX has minimal value of resulting velocity for the object after simulation. If velocity is below that value, physX sleeps object and energy appears lost. By experimenting with spheres of different mass and applying various impulses and forces I found out that this minimal velocity is 2 cm/s. Such behavior from physX is perfectly understandable and I am fine with it. Moreover, for any real project purposes Restitution value won’t be 1 and will represent some loss of energy which will make this 2 cm/s look like numerical noise :slight_smile:

Conclusion: linear motion is handled perfectly, momentum/energy conservation is honored precisely and so far everything can be prototyped in blueprints without diving into c++ and using physX API.

RigidBody study - angular motion

Just like with linear motion, SetPhysicsAngularVelocity should be avoided. Add Angular Impulse or Add Torque should be used.

Again, just like with linear motion, UE4 uses different units than SI.
Moment of inertia (I): kg*(cm^2)
Angular impulse (Iw): kg*(cm^2)(rad/s)
Torque: kg

Angular velocity (w): rad/s
1 rad/s = 57.29577951308 deg/s

Adding angular impulse.
You can see this as an instantaneous force applied that causes angular velocity change from w1 to w2. To compute impulse value you will need moment of inertia tensor values (can be acquired using node GetInertiaTensor). In the test below to make disc rotate around X axis with velocity of 35 deg/s, I multiplied X axis moment of inertia value in kg*cm^2 (2108660) by desired angular velocity converted to rad/s (35/57.2958). Resulting value is 12881106.


Adding torque.
Unlike impulse, torque needs to be applied over period of time. Simply divide computed impulse value by duration in seconds when torque will be applied. In my test to accelerate the disc to 35 deg/s over 2s, I need to use torque value of 12881106/2 = 6440553


I did some collision tests, including precession test:

Also, just like with linear motion, I figured sleep value for minimal angular velocity is: 0.0138 rad/s (0.79 deg/s)
While it can be seen that first example has perfect energy conservation and precession effect is also great, not everything is dandy. After performing numerous tests here are the key points:

  • CCD must be turned on. Otherwise collisions are missed when object has high angular velocity. Even if linear velocity is pretty small.
  • With CCD on high kinetic energy off axis collisions become elastic linear collisions, no angular motion is applied - I figured that this is fps dependent. When capped at 30 fps, >1KJ collisions behaved wrong. Higher fps or switching on SubStepping greatly improves things.
  • Total system energy is not conserved. Particularly on cases with multiple collisions. Energy lost/gained is pretty small though comparing to the total energy of the system before collision.

Well, I think I should stop bumping up this thread for now with these oddities which are not yet an actual WIP :slight_smile:
Final assorted notes before conclusion:

  • PhysX uses collision volume for computations. The actual mesh is irrelevant.
  • Having multiple meshes on blueprint using Auto Weld works just fine. However, only root object defines physics material, mass override can not be applied to children, only to the whole group.
  • Destroying a child or moving them around causes physX to recompute center of mass and moment of inertia tensor, real-time, this is pretty cool. However, angular momentum is not preserved. Object will maintain same angular velocity, its energy will just rise or drop.

Hopefully all this info will be of use to some who also just starting looking into UE4+physX physics model.
My conclusion is that physics model is really good and more than enough for my project needs. My next research will be thrusters/force/torque and actual flight model.

Thank you for writing these, they are extremely helpful!
Did you ever go on to research thrusters/force/troque? Would love if you could continue writing your findings :slight_smile: