I am bad at physics, but I want to implement a simplified but still physics based engine simulation. The code below works, in the sense that the engine speeds up when the throttle is down and slows down it’s up, but it’s not enough. The acceleration in my case is linear and instant. I want it to accelerate gradually due to the moment of inertia of the system. The same issue when the throttle is down. I want it to decelerate gradually. Please, try using classical mechanics - I want it to be physics based. Here’s the code that I have:
local RunService = game:GetService("RunService")
local module = {}
-- Constants
local INERTIA = 30*0.05 / 2 -- Rotational inertia of the whole drive: radius*mass/2.
-- Variables
local throttle = 0
local velocity = 0 -- Angular velocity of the powerplant in RPM
-- Functions
local function GetPowerTorque(velocity:number)
local torque = 2000
return torque
end
local function GetFrictionTorque(velocity:number)
local coefficent = 500
local threshold_start = 2000
if velocity < threshold_start then
return velocity * coefficent/threshold_start
else
return coefficent
end
end
function module.Throttle(state)
if state == Enum.UserInputState.Begin then
throttle = 1
else
throttle = 0
end
end
function module.Update(delta_time)
local initial_velocity = velocity * 2*math.pi/60 -- RPM to rad/s
local initial_momentum = initial_velocity * INERTIA
local impulse = GetPowerTorque(velocity) * throttle - GetFrictionTorque(velocity)
local angular_acceleration = impulse / INERTIA
local final_velocity = initial_velocity + angular_acceleration * delta_time
velocity = final_velocity * 60 / 2/math.pi -- rad/s to RPM
print("Velocity: "..velocity.." RPM")
end
return module
Also, I’m new to using ModuleScripts. I hope that it’s a good approach to do these calculations inside of it, but if not then tell me. THANKS!
Firstly what type of engine and what part of it are you trying to simulate? What is the coefficient? Also is this going to be fully physics based? Besides that the script looks fine, not much point into using a module script if this is all but if you were going to add more to this script try using meta tables and OOP considering this is a physics simulation.
For now it’s an internal combustion piston engine, but I want it to be able to use many types in the future.
The coefficient under the friction function is the friction coefficient.
The physics model is supposed to be simplified to a system that emulates a spinning mass. The GetPowerTorque will be representing the torque graph for the engine - it’s a placeholder for now.
The thing that I’m asking for, is how to use the moment of inertia to implement gradual acceleration and deceleration of the rotating mass.
I’m pretty sure your math is already flawless. You have an insanely high torque value compared to your inertial momentum, so of course the result is instantaneous acceleration.
At least when I stole your code and changed the torque/friction/mass values it started to accelerate/decelerate gradually:
local RunService = game:GetService("RunService")
local module = {}
-- Constants
local INERTIA = 30*2 / 2 -- Rotational inertia of the whole drive: radius*mass/2.
-- Variables
local throttle = 0
local velocity = 0 -- Angular velocity of the powerplant in RPM
-- Functions
local function GetPowerTorque(velocity:number)
local torque = 20
return torque
end
local function GetFrictionTorque(velocity:number)
local coefficent = 10
local threshold_start = 20
if velocity < threshold_start then
return velocity * coefficent/threshold_start
else
return coefficent
end
end
function module.Throttle(state)
if state == Enum.UserInputState.Begin then
throttle = 1
else
throttle = 0
end
end
local RPM2RAD = (2*math.pi) / 60
local RAD2RPM = 60 / (2*math.pi)
function module.Update(delta_time)
local initial_velocity = velocity * RPM2RAD -- RPM to rad/s
local initial_momentum = initial_velocity * INERTIA
local impulse = GetPowerTorque(velocity) * throttle - GetFrictionTorque(velocity)
local angular_acceleration = impulse / INERTIA
local final_velocity = initial_velocity + angular_acceleration * delta_time
velocity = final_velocity * RAD2RPM -- rad/s to RPM
print(("\n "):rep(25) .. "Velocity: "..velocity.." RPM")
end
return module
I would have to say that this approach will only support 1 engine running, because each time you require a module it will return the same table. Maybe consider making a module.new() function.
Another thing you might want to do is have a torque curve based on the current RPM of the engine as happens in real engines (they usually generate peak torque at the median engine range)
You could also have the throttle be a smoothly interpolated value, instead of it just being 0 or 1 (like a gas pedal)
No it’s not right, as if you change inertia to something like 100, the engine will start accelerating immediately. It should be sluggish and difficult to get that mass spinning. I believe that something is wrong with my physics. I’m sure that something has to be done with the impulse, initial_momentum and final_momentum. I know that final_momentum equals to initial_momentum + impulse, but that where my classical mechanics knowledge ends.
I just tried printing out the angular_acceleration, and it keeps getting lower… I don’t know what’s wrong with it:
function module.Update(delta_time)
local initial_velocity = velocity * 2*math.pi/60 -- RPM to rad/s
local initial_momentum = initial_velocity * INERTIA
local impulse = GetPowerTorque(velocity) * throttle - GetFrictionTorque(velocity)
local angular_acceleration = impulse / INERTIA
local final_velocity = initial_velocity + angular_acceleration * delta_time
local final_momentum = initial_momentum + impulse
velocity = final_velocity * 60 / 2/math.pi -- rad/s to RPM
print("Velocity: "..velocity.." RPM")
print("Acceleration: "..angular_acceleration.." rad/s^2")
end
return module
local RunService = game:GetService("RunService")
local RPM2RAD = (2*math.pi)/60
local RAD2RPM = 60/(2*math.pi)
local module = {}
-- Constants
local SHAFT_RADIUS = 30
local SHAFT_MASS = 1
local MOMENT_INERTIA = SHAFT_RADIUS * SHAFT_MASS * SHAFT_MASS * 0.5 -- Moment of inertia of a solid cylindrical prism about its axis of rotational symmetry (the crankshaft)
local THROTTLE_SNAP = 0.05
local FRICTION_S_CONSTANT = 10 -- static friction constant (simulates a required amount of torque to get the engine spinning, allows engine to reach 0 RPM)
local FRICTION_V_CONSTANT = 0.9 -- viscous friction constant (1st order friction for low speeds)
local FRICTION_Q_CONSTANT = 0.008 -- quadratic friction constant (2nd order friction for high speeds)
-- Variables
local avg_throttle = 0
local throttle = 0
local rpm = 0 -- Angular rpm of the powerplant in RPM
-- Functions
local function GetPowerTorque(rpm : number)
rpm *= 0.76
local vs = rpm * rpm
local torque = 0.0041*vs - 0.0000023*vs*rpm + 60
return torque -- just a proper torque curve like a car would have
end
local function GetFrictionTorque(rpm : number)
local rads = rpm * RPM2RAD
return FRICTION_Q_CONSTANT*rads*rads + FRICTION_V_CONSTANT*rads + FRICTION_S_CONSTANT
end
function module.Throttle(state)
throttle = (state == Enum.UserInputState.Begin) and 1 or 0
end
function module.Update(delta_time)
local initial_rads = rpm * RPM2RAD
avg_throttle = math.lerp(avg_throttle, throttle, THROTTLE_SNAP)
local impulse = GetPowerTorque(rpm) * avg_throttle - GetFrictionTorque(rpm)
local angular_acceleration = impulse / MOMENT_INERTIA
local final_rads = initial_rads + angular_acceleration * delta_time
rpm = math.max(0, final_rads * RAD2RPM) -- to simulate static friction simply, we have to clamp the RPM so it doesnt start spinning backwards
print(("\n "):rep(25) .. "thr: ".. avg_throttle)
print(("\n ") .. "acc: ".. angular_acceleration .." Rad/s")
print(("\n ") .. "rpm: ".. rpm .." RPM")
end
return module
I figured out most of the un-realistic feel in your code comes from a lack of semi-realistic frictional forces, and a semi-realistic torque curve is very important for the feel.
I also figured out you might have been using the wrong formula for moment of inertia (assuming you are actually making an engine, you’d want to get the inertial moment of the crankshaft)
I added static, viscous, and quadratic friction to your model. It seems to play a lot nicer.
I also made the throttle use a smoothed average instead of using the instant 0-1 snap