Librelancer 2022.02
-
-
-
https://www.youtube.com/watch?v=1RRxP4oQZZI
Glitchy New London THN aesthetic. (Click here, youtube embed still not working?)
-
Version 2017.10 is now released! (Check the first post for link)
-
Starship flight physics in Freelancer is pretty much Newtonian (barring rounding errors and of course the fact that masses don’t attract each other as they would in reality). The reason you stop when you throttle down is linear drag (compare to Stokes’ friction) and hardly a good model for atmospheric flight, really.
Anyway, when you kill your engine, the engine’s drag is not taken into account any longer, and so the only thing (literally) “dragging” you down anymore is your ship’s linear drag which tends to be rather small by comparison (in vanilla it’s 599 on the engine and 1 on the ship, if memory serves).
So if x( t ) is your location at time t, m your total mass, b( t ) your raw thrust force (be it from engine, or thruster, or indeed any other accelerating influence, like explosions can be, or the sum of any of the above) as a function of time t, v0 your velocity at t = 0, and d your linear drag (be it from engine, or ship, or the sum of both), then your velocity v( t ) as a function of time t is given as
v( t ) = v0 + ( b( t ) * t - d * x( t ) ) / m ,
<=> dx(t) / dt + d / m * x( t ) = v0 + b( t ) / m * t .
This is an inhomogenous linear first order differential equation with constant coefficients. An implementation of this sort of mechanics of course doesn’t require understanding the theory behind forms like these. Suffice it to say that our b is bounded (there is, after all, a maximum thrust for any accelerator in the game, and only a finite amount of them can be effective at once because computers have finite memory) and if we keep it constant at any value, then in the long run our speed approaches the familiar old thrust/drag ratio c = b / d as I will demonstrate now, along with what I mean by “the long run” and how it is a matter of the ratio j = d / m of drag and mass:
Assuming that your thrust b is constant, and that we start at the location x0 = 0, the exact analytic solution x( t ) to our motion equation is
x( t ) = c * t + ( c - v0 ) * ( exp( -j * t ) - 1 ) / j .Already we see j both scaling how far x deviates from a linear expression, as well as how quickly that linear expression is reapproached. x is also shifted by something proportional to the difference of thrust/drag and starting velocity. Let’s see how velocity behaves:
v( t ) = dx(t) / dt = c - ( c - v0 ) * exp( -j * t ) .
We could see it before, but now it’s a lot more obvious: At t = 0 our velocity is exactly v0. As t gets very big, the latter term vanishes almost completely, effectively only leaving behind The promised thrust/drag. We also see that j scales how quickly the latter term vanishes even more clearly now. We can now choose a speed w between v0 and c but not equal to c, and find out how long it takes for our ship’s speed to pass by it as it keeps approaching
t( w ) = ( log( c - v0 ) - log( c - w ) ) / j ,where ‘log’ denotes the natural logarithm.Now, of course, there are rounding errors due to the discretion of values inherent in digital computing systems. Not even space in vanilla Freelancer is truly continuous which is why we see our ship jitter ever more as we move further away from the system’s center. The formalism I presented is an idealized, mathematical, analytical model, and as we measure the actual motion we are bound to find slight errors and imprecisions, but this is, as far as I know, how Freelancer’s physics are at least supposed to behave.
-
I don’t understand most of what you said, but I’m pretty sure I based my physics off of an old post you wrote somewhere on some part of the internet that probably doesn’t exist anymore. It was a description of Freelancer’s physics and included simple formulas for calculating stuff that I implemented into my “Freelancer simulator.”
In terms of linear stuff, it seems to have worked perfectly and mimicked the quirks that happen when you set weird values in Freelancer. For example, plugging in vanilla values gave speeds and accelerations that seemed to line up 1:1 to Freelancer. Ditto for when I plugged in crazy values like what’s used in 88 Flak for its very weird take on Freelancer’s physics.
What tripped me up was how rotational forces work. I essentially made something up that if I recall correctly was the same ideas as translational, just applied to rotations. When in cockpit view, where you have more direct control of the ship, it seemed to line up very closely, but it was very slightly off in a way I don’t recall. Again, using both vanilla and Flak values, I got something that felt close enough that you’d need a stopwatch to really tell the difference.
Where it got weird though was in third person. I suspect that you aren’t actually flying your ship in third person, and instead the AI is flying it. I did tests in Freelancer and noticed that your turn rate is actually faster in third person when compared to the cockpit. My turn rates matched the cockpit turn rates, but not the third person ones. I suspect that when the ship banks in third person, the ship is now using a part of its pitch torque to turn. The combined power of the yaw torque and a slight pitch torque means it turns a bit faster.
That becomes a question of control though at that point, and not physics, but I thought it was very interesting.
Edit:
Yes, there are also a lot of particulars and quirks if you want to emulate Freelancer 1:1, especially when it comes to engine kill and cruise.
For engine kill, you switch to using only the ship’s inherent linear drag. The engine turns off, and when it turns off it also stops providing drag. However, if you use the thruster or strafe, the engine must turn back on so that you maintain your limited top speed. If you don’t turn it back on, then using the thruster you’ll be able to accelerate to whatever top speed is implied by your linear drag. Using vanilla values, it ends up being effectively unlimited because it’s only 1.
Cruise gets weird as well. Cruise has a set speed to reach, but it still uses forces to achieve those speeds. At least, I think it does. That’s how I wrote it anyway. I have to do some pre-emptive calculations, but I figure out how much thrust is required to reach cruise speed (300 in vanilla’s case) when the engine is on (so engine + ship drag). Then, when the cruise engines turn on, I ramped up this additional cruise engine force to slowly get to that pre-calculated value. If you just apply the cruise engine force instantaneously, then you don’t get the smooth ramp up to speed like you do in vanilla FL. You know how when you drop out of cruise you decelerate to your normal speed almost instantaneously? The same exact thing happens in reverse when you just apply the extra cruise engine thrust on its own.
-
Why485, Gisteron: Thanks for your input. The next gameplay demo will include semi-accurate physics based on this.
Here’s for something fun, a couple of mishaps on planet thns!
-
I have played with ship engines and physics extensively, as you will be able to see in my mod, which I hope to release in the next months.
Those experiments gave me a good understanding of how Freelancer does this.
First of all, you can have more than one engine in Freelancer. If that is the case, Freelancer only uses the linear_drag from the first engine, but the force and power requirements are properly added by Freelancer.
Cruise mode:
Freelancer ramps up the force value of the first engine to reach the cruising_speed from constants.ini in cruise_accel_time from constants.ini. Aside from that, all other engines provide their max_force. If you run out of energy you will decelerate to a proportional value, e.g. if you generator can only supply one half of the required energy, you will slow down to the half of cruising_speed once your buffer is empty. If you want different drag values while in cruise, you may alter cruise_drag in constants.ini (default is 1)Engine kill: The force and linear_drag of the first engine are turned off, so only the ships linear drag is slowing you down in this mode. All other engines continue to provide force (not cool, but FL does it this way)
@Gisteron, I don’t think that FL’s calculations are that complex. I did a test run with a ship this week, but I haven’t found time to analyse it yet.
See also:
engine_equip.ini
constants.ini
shiparch.ini -
Gisteron’s analysis is all higher order equations which are completely pointless to use in an actual implementation of the game. You can reach parity just by iteratively simulating the ship’s handling each update using very simple linear equations.
velocity += front_vector * thrust_force * delta_t / mass; velocity -= velocity * linear_drag * delta_t / mass; position += velocity * delta_t; angular_velocity += steering_vector * steering_torque * delta_t; angular_velocity -= angular_velocity * angular_drag * delta_t; orientation *= RotationMatrixFromEulerAngles(angular_velocity * delta_t);
Most values here are fairly self-explanatory. The front vector points towards the front of the ship. The steering vector is a 3-component vector indicating the amount of yaw, pitch and roll to apply (it’s directly determined from the mouse cursor’s direction and whether the roll key is pressed), varying from -1 to 1. [c]RotationMatrixFromEulerAngles[/c] is essentially just Freelancer’s [c]RotateMatrix[/c] which just calculates a rotation matrix using the appropriate coordinate system from Euler angles, you might need to adjust the coordinate order and convert to degrees from radians.
The only trick not mentioned above is that you don’t want your correction factor (the drag) to cause the ship to move in the other direction. This is just a simple clamp that can be done in various ways, I just do
if (sgn(velocity.x - drag.x) != sgn(velocity.x)) velocity.x = 0; else velocity.x -= drag.x; ```for both velocity and angular velocity, component-wise, where [c]sgn[/c] is the sign function. If you want added precision, you can run this whole calculation using double precision floating point values ([c]double[/c]) and you can also iteratively run it many times each update. Since Freelancer has a variable update time rather than a fixed one, I target an update every 1/120th of a second and calculate the update function as many times as necessary (e.g. if the update was called 1/60th of a second after the last one, I run the computations twice with the same inputs). This allows the calculation to be a lot smoother and probably accounts for the ~1% variation with Freelancer's values (which are comparatively extremely noisy) in testing.
-
Is the drag reversing velocity sign something that happens due to discretion of time or rounding errors or the like? I ask because analytically that shouldn’t ever happen, but of course time intervals between computations are not infinitely small, and so if one takes v0 to be the velocity that was a finite time ago (say, a hardcoded fraction of a second, or a frame), I can see how such unwanted reversals might happen and one would need to clamp like that. Is that why they do?
As for angular motion, the parameters seem to be the same, except now you have one set of torque, inertia, and angular drag for each of your three rotational axis, as counterparts to thrust force, mass, and linear drag, respectively.
-
Yes, it’s why. It should be fairly obvious that discretization will introduce errors, the most pronounced of which is oscillations.
-
Here’s the current version of the ship control + steering. Still missing engine kill and cruise ramping though those shouldn’t be too difficult. The steering is almost there @FriendlyFire do you know if FL calculates a roll input when you’re only using mouse (and not the roll keys) ?. There’s also the auto-levelling of the ship that is a bit dodgy but it does level.
-
As far as I could tell, the game only produces roll when the roll key is pressed or the autopilot is engaged. I believe autoleveling will also use it, but I have autolevel disabled so I can’t say for certain.
-
So I took the time to evaluate my test and here are the results:
My test parameters were:
max_force = 10000
linear_drag = 10
mass = 250How I performed the test:
I put in reverse thrust until my speed was about -50. At that speed I engaged full thrust and started to gather the data once I went over 0 m/s. I recorded the whole test at constant 30FPS.I used ffmpeg to dump the videostream as pictures, put the displayed speed of every tenth picture in a table and plotted it.
The calculated speed line was generated by the following equation(v0 is the speed of the previous iteration):
v = v0+(max_force-v0linear_drag)/mass
so
a = (force-vlinear_drag)/massThe delta curve is just the difference of the other two curves, usually ~3 m/s.
-
Having fun starting to implement bases - here’s the current state of Planet New Berlin!
-
First attempt at docking:
https://www.youtube.com/watch?v=Oxyc7dTkhZY
It’s about 25% implemented at the moment. It only picks the first hardpoint (doesn’t follow HpDockA01,02 etc.) and there’s no collision avoidance. But progress is progress!
(Thanks to Why485 and Treewyrm for invaluable information)
-
https://www.youtube.com/watch?v=JSjwyvMDCjs
Docking part 2:
- Now follows the 02 and 01 hardpoints
- Cmp animated door that works with the physics engine.