Calculating Drag HELP

Hey there, I am making an airship that uses vector forces to move. I am attempting to calculate drag but I keep getting weird numbers. Does anyone know how I should go about calculating drag? I am not looking for it to be incredibly realistic just want it to slow my ship down more as the ship speeds up. Thank you!

EDIT: After looking back at this later I wanted to share the equation I used. It is relatively efficient and looks pretty good as well it also can be easily changed values.

dragForce = (force/dragCoefficient) / (maxSpeed^2)

3 Likes

Not sure if you’ve seen this equation before (or if it works cuz I’m not good at math lol), but here it is:

https://www.grc.nasa.gov/www/k-12/rocket/drageq.html

It’s by NASA so I trust em, results should probably good. Idk how you’d use this equation in Lua but good luck.

EDIT: Just found a little explanation in this post, it may help you out

Yea, I’ve seen that. What I am doing right now is subtractting the ships velocity from its speed multiplied by a constant

I’ve had to deal with drag before, and its actually not as bad as you might think. Simplifying the drag equation to its most imporant aspects, we get:

Drag = DragCoefficient * (Velocity.Magnitude ^ 2) * DragForce * -Velocity.Unit

  • DragCoefficient is a coefficint that describes how well an object can travel though space, it has no unit of measurement but is instead more like… a percentage. A cone has a lower coefficient going forward nose-first, than base-first

  • Velocity squared describes the relationship between speed and air resistance.

  • DragForce is arbitrary value just helps in tuning the force

  • -Velocity.Unit is the direction that the force is applied, opposite to the velocity of the object (because its slowing down)

(we dont need to consider air pressure or area of a surface as they are usually consant)

Simple enough, but as the drag coefficient is only constant on a sphere (which on a Zeplin is probably all you really need), Its a bit more work to calculate.

For every axis the object moves in (3), you need to describe a “Drag value”. A plane moving forward (-z), has less drag than a plane moving sideways (x). Then multiply the “Drag Value” times the velocity on the local axis, that is the coefficient for that axis. Not that bad is it?

And lastly, the find the local velocity on an axis, use:

local LocalVelocity = Part.CFrame.Rotation:Inverse() * Part.AssemblyLinearVelocity

hope this helps! :grin:

3 Likes

Note: Even if I may have gone a little bit over the top with this, it’s still not perfectly realistic

Lucky for you, I just finished my aerodynamics script.

Okay basically, it uses some math and the size of the model to draw an outer box of positions (shown as attachments below)
image
→
image

Then each of these attachments raycasts inwards (represented by this yellow line) halfway into the model.
image
Halfway since, usually if it’s a symmetrical/centered model it will only take half of whatever side it’s on to hit something. (You can easily change it to 100% of the side’s length)
The spots where ray’s hit then have attachments placed on them (for actual purposes this time instead of visualization), and then are stored into a table for later.
image
As you can see, it’s not perfect since it’s a very off center model.
However, if we use something like a sphere (padding = 0.5 studs):
image
Or a Robloxian Home (padding = 2.5 studs):
image

Alongside each attachment a linearvelocity is also created, then parented inside of it.
image

Okay, we’re done with all the fancy attachment stuff. After that it gets pretty simple.

First you should probably know some units.

  • 0.28 Meters = 1 Stud (Image below from Game Settings → World)
    image
    image

  • 1 Kg ~ 8 Roblox Mass Units (approximately because the real value is somewhere around 8.13185364880308 shown with default gravity.)


    Equation: Earth Gravity (in studs) / Roblox Gravity (in studs) * 1 meter cube of part with density of 1.

Let’s get on with the calculations.
If we type in “drag formula” into the google search bar, the first equation that comes up is the one provided by Wikipedia on the sidebar.
image
There’s also one by NASA, but it’s just the same, and looks less fancy, so I’ll stick to the one above.
image

Alright, first up is p. Or density of fluid. Since we’re likely flying airships through air, let’s find that first.

The only formula I could find that only needed the altitude in meters for air pressure was from Quora. Which isn’t the best but we’ll make due with it.
image

Putting this together gives us

local p = (101.325 * (1 - ((0.0065 * h) / 288.15)) ^ ((9.80665 * M) / (8.31447 * 0.0065)))

Now there’s a few issues, here. First of all, this is using earth gravity.
We can fix this by doing (workspace.Gravity * 0.28) to convert the gravity of the workspace into meters. Next up, what’s M? Well after a quick google search it turns out to be molar mass. So we need the molar mass of air. Easy enough, one more shows it to be 28.96g/Mol (or more exactly 28.9628g/Mol). I’m not exactly sure what the units here stand for, so please correct me on this. I’m assuming that the g in g/Mol stands for grams, and since the rest of the constants for the equations are in meters and such, and it’s usually kg/m^3 instead of g/m^3 when checking density, I’m gonna go ahead and assume it’s meant to be kg/Mol. So we would go ahead, and divide the numerator by 1,000. And we get: 0.0289628Kg/Mol.
Now we can plug that in:

local p = (101.325 * (1 - ((0.0065 * h) / 288.15)) ^ (((workspace.Gravity * 0.28) * 0.0289628) / (8.31447 * 0.0065)))

Since we are going to be substituting h for Position.Y we can just use that, and convert it to meters.

local p = (101.325 * (1 - ((0.0065 * (Position.Y / 0.28)) / 288.15)) ^ (((workspace.Gravity * 0.28) * 0.0289628) / (8.31447 * 0.0065)))
--to convert from meters to studs you multiply by 0.28, so to convert from studs to meters you divide by 0.28

Alright, all looks good and all when we test this in the output:


However an issue comes up when you go above a certain altitude between 10,000 and 13,000 studs. I’ve gone ahead behind the scenes and found the magic number where it switches from real numbers to NaN, it seems that 12412.615384615384 is the breaking point. We can avoid this by clamping with some simple logic:

local p = (101.325 * (1 - ((0.0065 * ((Position.Y > 12412.615384615384 and 12412.615384615384 or Position.Y) / 0.28)) / 288.15)) ^ (((workspace.Gravity * 0.28) * 0.0289628) / (8.31447 * 0.0065)))

Basically this says, if altitude > magic number then be magic number instead, if not be altitude

Why not clamp the lowerbound too? you may ask. Well, if we plug in negative numbers to this equation, the air pressure becomes greater (and it doesn’t have a limit, it can go past the negative version of the magic number).


Meaning if you kept falling and falling, there would be a point where you would have enough force to go further down, basically you would start bobbing up and down and float like if you plummeted into the ocean (this effect doesn’t occur at sea level though). I haven’t actually gone ahead and tested this myself so I’m not sure what exactly what would happen.

ok. thats cool, but what do we do we these arbitrary numbers that this equation spits out?
So the thing is, I don’t know. So we’ll turn the equation into the air density at the current altitude.
(It’s much easier than you think)
If we examen our equation, (101.325 * (1 - ((0.0065 * ((Position.Y > 12412.615384615384 and 12412.615384615384 or Position.Y) / 0.28)) / 288.15)) ^ (((workspace.Gravity * 0.28) * 0.0289628) / (8.31447 * 0.0065))), it seems everything after the 101.325 is just the percentage of how much air there is relative to the amount at sea level.


(If you multiply by 100 it would be ~99.763%)
Using this, we can just grab our air density at sea level from google, which happens to be 1.293kgm^-3 or 1.293kg/m^3 coming from NASA here.
Now all we do is plug it in at the front of our percentage, replacing the 101.325 giving us this:

local p = (1.293 * (1 - ((0.0065 * ((Position.Y > 12412.615384615384 and 12412.615384615384 or Position.Y) / 0.28)) / 288.15)) ^ (((workspace.Gravity * 0.28) * 0.0289628) / (8.31447 * 0.0065)))

This also fixes the problem I didn’t mention where kPa was equal to kiloNewtons/m^2. m^2. Density is measured in m^3.

Note: I forgot one last thing, since we clamp to 12412.615384615384 studs in altitude, anything equal or over that will make the equation return 0 density for air. If this is an issue, say your airships or whatever are flying really high in the sky, we can do this:

local SeaLevel = 0 --0 is the default, nothing changes (in studs of course)
local p = (1.293 * (1 - ((0.0065 * (((Position.Y - SeaLevel) > (12412.615384615384 + SeaLevel) and 12412.615384615384 or (Position.Y - SeaLevel)) / 0.28)) / 288.15)) ^ (((workspace.Gravity * 0.28) * 0.0289628) / (8.31447 * 0.0065)))

Anyways with that out of the way, we can move on to the next variable in our drag formula. Oh. Oh velocity? Should be pretty easy enough. All we need to do is:

local Difference = (OldPosition - Position) --important that we have it reversed as Origin - Destination instead of Destination - Origin for later (we get to remove one minus sign)
local Velocity = Difference.Magnitude * 0.28 --since magnitude is absolute it wont be affected by the remark above

Alright so, next is Drag Coefficient.
Googling it shows this formula.
image
Usually this would be really hard to calculate, and at first when I saw this I was like “WHHAAA, We’re solving for Fd in the first place!!!”
Well, remember our trusty attachment system we set up?
image

Well, if an object is moving, it should have a previous position. There should also be a direction to move in to get to that direction. Unfortunately, I did a bad job of explaining about the attachment table. So I’ll do that right now.

So basically, there’s a variable alongside the attachment table called AttachmentCount. and Since the attachment table is a dictionary where each index is the attachment instance and the value is the current WorldPosition of the attachment. (type = {[Attachment]: Vector3}). Because of this, we can’t do #Attachments. Since the # operator only works on tables with numerical indexes. So for every attachment we add, we just do:

AttachmentCount += 1

Alright, now that that’s covered, what we can do, is every RunService HeartBeat. In each heartbeat, define the dragCoefficient of the current frame, and on second thought the projected area.
Both of them are basically done at the same time due to their nature, so why not knock out the last two variables at the same time?

The projected area is kinda hard to explain, but for example if you were to look at a sphere from the side, you wouldn’t see the entire 3 dimensional sphere, but instead a part of it. Since it’s a sphere, you are able to see half of it at once. Which also happens to be it’s drag coefficient, 0.5.

So the plan is, is on each heartbeat, loop through every single attachment.
We’re then going to raycast backwards against the direction we are moving from each attachment. This way, if the ray hits, we know we are in front of our model and dragging it back with drag :cool:.
If the ray hits, we have to increment both the drag coefficient and projected area by a certain number. But what number?
Well since our drag coefficient is kind of like a percentage between 0 and 1, we can increment it up by the reciprocal of how many attachments we have. That way, if say, exactly 1/2 of all of our attachment’s have their rays hits, the drag coefficient will be 0.5, or 1/2. Same goes for 3/4, and etc. with any decimal in between 0 and 1.

Next is our projected area.
image
In the image above, we have a square. Let’s imagine each attachment as part of a square on the surface of our model. The value or area of each square would be the side length squared. We can find the side length by getting the distance between an attachment and one of it’s neighbors.
image
However, since the distance between each attachment is defined by the padding, we don’t have to figure this out. Okay, that’s cool. But we want the value of each attachment, not the entire square. Since a square has four vertices, four attachments, we can divide the area by 4 and distribute it to each attachment. Meaning that each attachment is worth Padding ^ 2 / 4.

Alright so let’s code this last part.

local Kg = ((9.80665 / 0.28) / workspace.Gravity * 0.28 ^ -3) --our Kg from earlier

local reciprocal = 1 / AttachmentCount
local paddingUnitArea = Padding ^ 2 / 4

game:GetService("RunService").Heartbeat:Connect(function(dt)
		local dragCoefficient = 0
		local projectedArea = 0
end)

First we’re doing that, next we have to raycast. Since we are going to be raycasting for potentially thousands of attachments, we want the ray to be as small as possible in order to be as inexpensive as possible. Since the attachment is directly on top of the model’s surface, we only have to raycast 0.001 studs in the opposite direction we’re moving:

--these are the same raycast params used to raycast attachments onto the model's surface
local Params = RaycastParams.new()
Params.FilterDescendantsInstances = {Model}
Params.FilterType = Enum.RaycastFilterType.Include
Params.RespectCanCollide = true
Params.IgnoreWater = true

local Kg = ((9.80665 / 0.28) / workspace.Gravity * 0.28 ^ -3)

local reciprocal = 1 / AttachmentCount
local paddingUnitArea = Padding ^ 2 / 4

game:GetService("RunService").Heartbeat:Connect(function(dt)
			local dragCoefficient = 0
			local projectedArea = 0
			for Attachment, oldPos in pairs(Attachments) do
				if Attachment.WorldPosition ~= oldPos then --this if statement is SUPER important, if your model stops moving and you do not have this, it will attempt to get the direction and return NaN, messing up the drag, and then the model just disappears from NaN velocity
					local results = workspace:Raycast(Attachment.WorldPosition, ((oldPos - Attachment.WorldPosition).Unit * 0.001), Params) 
					--^^ remember from earlier how it was reversed?

					if results then
						dragCoefficient += reciprocal
						projectedArea += paddingUnitArea
					end
				end
			end
		end)

And yeah! That’s basically all the variables defined.
Now we just have to put it all together:

	local Kg = ((9.80665 / 0.28) / workspace.Gravity * 0.28 ^ -3)
	
	local reciprocal = AttachmentCount ^ -1
	local paddingUnitArea = Padding ^ 2 / 4
	game:GetService("RunService").Heartbeat:Connect(function()
		local dragCoefficient = 0
		local projectedArea = 0

		for Attachment, oldPos in pairs(Attachments) do
			if Attachment.WorldPosition ~= oldPos then
				local results = workspace:Raycast(Attachment.WorldPosition, ((oldPos - Attachment.WorldPosition).Unit * 0.001), Params)

				if results then
					dragCoefficient += reciprocal
					projectedArea += paddingUnitArea
				end
			end
		end
		--commence my original notes below before i started making this post
		for Attachment, oldPos in pairs(Attachments) do
			if Attachment.WorldPosition ~= oldPos then
				local Difference = (oldPos - Attachment.WorldPosition) 
				--^^usually its destination - origin, but this way its origin - destination so the direction is automatically negative, but since magnitude is absolute i dont have to worry about that
				
				--if i multiply the height in studs by the reciprocal of 0.28 then the altitude will be higher to account for using all real life units
				--that way so i dont have to convert every single term in the equation below into roblox units, i only have to convert the altitude
				local airDensity = (1.293 * (1 - ((0.0065 * ((Attachment.WorldPosition.Y - Sealevel) > (12412.615384615384 + Sealevel) and 12412.615384615384 or (Attachment.WorldPosition.Y - Sealevel)) * 0.28^-1) / 288.15)) ^ (((workspace.Gravity * 0.28--[[9.80665]]) * 0.02896) / (8.31447 * 0.0065)))
				local velocity = Difference.Magnitude * 0.28
				--the reason i commented out one of the terms for the exponent for air density was because it was earth's gravity, so i just changed it to roblox gravity in meters (workspace.Gravity * 0.28)
				local Drag = ((Kg * 3.903325) * airDensity * velocity ^ 2 * dragCoefficient * projectedArea)
				--i didn't mention terminal velocity but i'll do it below, its pretty easy and super important
				--(super important meaning if u don't do it u get sent flying millions of studs away)
				local terminalVelocity = ((2 * Kg * Mass * workspace.Gravity)--[[(78.4532 * Mass * workspace.Gravity)]] / (airDensity * dragCoefficient * projectedArea)) ^ 0.5
																		     --^^ commented out because its basically earth gravity (in studs) * roblox gravity * mass 
				Attachment.LinearVelocity.MaxForce = terminalVelocity
				Attachment.LinearVelocity.VectorVelocity = Drag * Difference.Unit
				--^^ * Difference.Unit that way so if you are moving in any direction the drag should apply opposite
				Attachments[Attachment] = Attachment.WorldPosition
			end
		end
	end)

Alright yeah, so that terminal velocity. Forgot to mention it. It’s pretty easy, all you do is caculate it then set the max force of the LinearVelocity to the terminal velocity.

This is the formula, as you can see, we basically have all the variables except Mass
image
Mass is pretty easy to get though, I just put this at the top of my script and ta-da!:

	local Mass = 0
	for _, Part in pairs(Model:GetDescendants()) do
		if Part:IsA("BasePart") then
			Mass += Part:GetMass()
		end
	end

Now all that leaves is, nothing… We’re done! Yay! My brain can finally relax after nine hours of typing this. Maybe I can regrow braincells by sleeping.

Before I post this though, here’s the full script if you wanna try it out yourself.
You can follow the comments and put it inside a model, or require it from another script and call the function on a model. I recommend letting the padding go to the default, but you can mess around with it, just don’t make it too small, because depending on the model’s size, so many raycasts will happen that your studio crashes.

It’s here in all of it’s glory for you to dissect and stuff, so enjoy. I hope this post helped! Feel free to ask my any questions or anything.

--// In a module script, you can just remove the outer function and change-
--// -the parameters into variables
return function(Model: Model, Padding: number?, Sealevel: number?)
	
	Sealevel = Sealevel or 0

	local Size = Model:GetExtentsSize()
	local originCFrame = Model:GetBoundingBox()
	
	Padding = Padding or (Size.Magnitude / 20)
	
	local Mass = 0
	for _, Part in pairs(Model:GetDescendants()) do
		if Part:IsA("BasePart") then
			Mass += Part:GetMass()
		end
	end

	local Locations = {}

	local Vectors = {X = "LookVector", Y = "UpVector", Z = "RightVector"}

	local Params = RaycastParams.new()
	Params.FilterDescendantsInstances = {Model}
	Params.FilterType = Enum.RaycastFilterType.Include
	Params.RespectCanCollide = true
	Params.IgnoreWater = true

	local function FillSide(Face: Vector3)
		local Sides
		if string.find(tostring(Face), "%-") then
			Sides = Vector3.new(-1, -1, -1) - Face
		else
			Sides = Vector3.new(1, 1, 1) - Face
		end
		local Axis1, Axis2
		for Axis, V in pairs({X = Sides.X, Y = Sides.Y, Z = Sides.Z}) do
			if V ~= 0 then
				if not Axis1 then
					Axis1 = Axis
				else
					Axis2 = Axis
					break
				end
			end
		end
		local AxisE
		for Axis, V in pairs({X = Face.X, Y = Face.Y, Z = Face.Z}) do
			if V ~= 0 then
				AxisE = Axis
				break
			end
		end
		for A1 = -(Size[Axis1] / 2), (Size[Axis1] / 2), Padding do
			for A2 = -(Size[Axis2] / 2), (Size[Axis2] / 2), Padding do
				local A1P = Sides[Axis1] * originCFrame[Vectors[(AxisE == "Y" and Axis2 or Axis1)]] * A1
				local A2P = Sides[Axis2] * originCFrame[Vectors[(AxisE == "Y" and Axis1 or AxisE)]] * A2
				local AEV = Face[AxisE] * originCFrame[Vectors[(AxisE == "Y" and AxisE or Axis2)]] * (Size[AxisE] / 2)

				Locations[(originCFrame + (A1P + A2P + AEV))] = {Face, AxisE}
			end
		end 
	end
	
	-- making an outer box of positions that will soon raycast inwards to put attachments on the surface of the model
	FillSide(Vector3.new(1, 0, 0))
	FillSide(Vector3.new(0, 1, 0))
	FillSide(Vector3.new(0, 0, 1))
	FillSide(Vector3.new(0, 0, -1))
	FillSide(Vector3.new(0, -1, 0))
	FillSide(Vector3.new(-1, 0, 0))
	


	local Attachments = {}
	local AttachmentCount = 0
	local Rotation = {X = CFrame.Angles(0, math.rad(90), 0), Y = CFrame.Angles(0, math.rad(90), math.rad(180)), Z = CFrame.Angles(math.rad(180), math.rad(90), 0)}
	for CF, T in pairs(Locations) do
		local dir = ((originCFrame * Rotation[T[2]])[Vectors[T[2]]] * T[1][T[2]] * Size[T[2]] / 2)
		local results = workspace:Raycast(CF.Position, dir, Params)

		if results then
			local Attachment = Instance.new("Attachment")
			Attachment.Name = "DYNAMIC_ATTACHMENT"
			Attachment.Parent = results.Instance
			Attachment.WorldPosition = results.Position
			local LinearVelocity = Instance.new("LinearVelocity")
			LinearVelocity.Attachment0 = Attachment
			LinearVelocity.RelativeTo = Enum.ActuatorRelativeTo.Attachment0
			LinearVelocity.Parent = Attachment

			AttachmentCount += 1
			Attachments[Attachment] = results.Position
		end
	end
	
	--UNITS:
	--1 KG = 0.125 Roblox Mass Unit
	--1 Stud = 0.28 meters (or 1 meter = 3 + 4/7 studs)
	
	--local p = (101.325 * (1 - ((0.0065 * (h > 12412.615384615384 and 12412.615384615384 or h) * 0.28^-1) / 288.15)) ^ ((9.80665 * 0.02896) / (8.31447 * 0.0065)))
	--air density in newtons per square meters^^, convert to kg/m^3

	--new, updated, kg/m^3 version:
	--local p = (1.293 * (1 - ((0.0065 * ((h - Sealevel) > 12412.615384615384 and 12412.615384615384 or (h - Sealevel)) * 0.28^-1) / 288.15)) ^ ((9.80665 * 0.02896) / (8.31447 * 0.0065)))
	--^^ replaced 101.325 (sea level i think) with 1.293 because 1.293 is the density of air or smth?
	--since air density is in kg/m^3 units instead of the sea level thingy the output should be those units too cuz its using them
	
	--local Reciprocal = (#Attachments) ^ -1
	--Cd += reciprocal --Cd or drag coefficient ranging from 0 to 1
	--A += padding^2/4

	--multiply drag force by 9.80665 * 8 or 78.4532 to convert to Roblox Mass Units
	--EDIT: earth gravity ^^, roblox gravity would be workspace.Gravity * 2.24 (8 * 0.28 = 2.24)
	--^^ studs/second
	--multiply by dt to make it consistent, nvm dont do this since velocity is already measured in studs/dt instead of studs/s
	
	local Kg = ((9.80665 / 0.28) / workspace.Gravity * 0.28 ^ -3)
	
	local reciprocal = AttachmentCount ^ -1
	local paddingUnitArea = Padding ^ 2 / 4
	game:GetService("RunService").Heartbeat:Connect(function()
		local dragCoefficient = 0
		local projectedArea = 0

		for Attachment, oldPos in pairs(Attachments) do
			if Attachment.WorldPosition ~= oldPos then
				local results = workspace:Raycast(Attachment.WorldPosition, ((oldPos - Attachment.WorldPosition).Unit * 0.001), Params)

				if results then
					dragCoefficient += reciprocal
					projectedArea += paddingUnitArea
				end
			end
		end

		for Attachment, oldPos in pairs(Attachments) do
			if Attachment.WorldPosition ~= oldPos then
				local Difference = (oldPos - Attachment.WorldPosition) 
				--^^usually its destination - origin, but this way its origin - destination so the direction is automatically negative, but since magnitude is absolute i dont have to worry about that
				
				--if i multiply the height in studs by the reciprocal of 0.28 then the altitude will be higher to account for using all real life units
				--that way so i dont have to convert every single term in the equation below into roblox units, i only have to convert the altitude
				local airDensity = (1.293 * (1 - ((0.0065 * ((Attachment.WorldPosition.Y -
 Sealevel) > (12412.615384615384 + Sealevel) and 12412.615384615384 or (Attachment.WorldPosition.Y - Sealevel)) * 0.28^-1) / 288.15)) ^ (((workspace.Gravity * 0.28--[[9.80665]]) * 0.0289628) / (8.31447 * 0.0065)))
				local velocity = Difference.Magnitude * 0.28
				--the reason i commented out one of the terms for the exponent for air density was because it was earth's gravity, so i just changed it to roblox gravity in meters (workspace.Gravity * 0.28)
				local Drag = ((Kg * 3.903325) * airDensity * velocity ^ 2 * dragCoefficient * projectedArea)

				local terminalVelocity = ((2 * Kg * Mass * workspace.Gravity)--[[(78.4532 * Mass * workspace.Gravity)]] / (airDensity * dragCoefficient * projectedArea)) ^ 0.5
																		     --^^ commented out because its basically earth gravity (in studs) * roblox gravity * mass 
				Attachment.LinearVelocity.MaxForce = terminalVelocity
				Attachment.LinearVelocity.VectorVelocity = Drag * Difference.Unit
				--^^ * Difference.Unit that way so if you are moving in any direction the drag should apply opposite
				Attachments[Attachment] = Attachment.WorldPosition
			end
		end
	end)
end

Left side is before falling, and right side is mid-fall during runtime. (With attachment and force stuff visibility enabled)
image

Edit: I’m pretty sure you aren’t going to use this exact method since it take’s a second or two for the script to fully setup, and it may not give the exact results you want. However, it’s definitely a good starting place for ideas on how you should go about calculating drag.

7 Likes

Thank you, this is similar to how I was already doing it just a bit more realistic and simpler lol, this seems to work perfectly while being quick and relatively efficient, you’re a life saver.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.