Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
COMMUNITY QUESTION

How do you change air friction?

So... I looked back into long-ago questions and I saw swimming in the air... and I made this LocalScript in StarterPlayerScripts.

wait()
local player = game:GetService("Players").LocalPlayer
wait(2)
spawn(function() while true do
	player.Character.Humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
	game:GetService("RunService").RenderStepped:Wait()
end end)
player.Character.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Swimming, true)
for i,v in pairs(Enum.HumanoidStateType:GetEnumItems()) do
	if v ~= Enum.HumanoidStateType.Swimming then
		player.Character.Humanoid:SetStateEnabled(v, false)
	end
end

wait()
spawn(function() while true do
	player.Character.Humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
	game:GetService("RunService").RenderStepped:Wait()
end end)
wait()
spawn(function() while true do
	player.Character.Humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
	game:GetService("RunService").RenderStepped:Wait()
end end)
wait()
spawn(function() while true do
	player.Character.Humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
	game:GetService("RunService").RenderStepped:Wait()
end end)
for _, part in pairs(game:GetService("Players").LocalPlayer.Character:GetChildren()) do
	if part:IsA("BasePart") then
		local F = Instance.new("BodyForce", part)
		F.Force = Vector3.new(0, 196.2, 0) * part:GetMass()
	elseif part:IsA("Accoutrement") then
		local F = Instance.new("BodyForce", part.Handle)
		F.Force = Vector3.new(0, 196.2, 0) * part.Handle:GetMass()
	end
end--]]

The issue is, the BodyForces cause swimming in the air to be more like... well... swimming in the air; there's not enough friction.

How do I modify air friction here?

##Response to answer:

I changed the code to this

wait()
local player = game:GetService("Players").LocalPlayer
player.Character.Humanoid.WalkSpeed = 20
wait(2)
spawn(function() while true do
	player.Character.Humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
	game:GetService("RunService").RenderStepped:Wait()
end end)
player.Character.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Swimming, true)
for i,v in pairs(Enum.HumanoidStateType:GetEnumItems()) do
	if v ~= Enum.HumanoidStateType.Swimming and v ~= Enum.HumanoidStateType.None then
		player.Character.Humanoid:SetStateEnabled(v, false)
	end
end

wait()
spawn(function() while true do
	player.Character.Humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
	game:GetService("RunService").RenderStepped:Wait()
end end)
wait()
spawn(function() while true do
	player.Character.Humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
	game:GetService("RunService").RenderStepped:Wait()
end end)
wait()
spawn(function() while true do
	player.Character.Humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
	game:GetService("RunService").RenderStepped:Wait()
end end)

-- helper function so we can use a single bodyforce in rootpart 
-- instead of individual bodyforces per part
local function getSummedMass(model)
    local mass = 0 
    for _, v in pairs(model:GetDescendants()) do
        if v:IsA('BasePart') and v.ClassName ~= 'Terrain' then
            mass = mass + v:GetMass()
		elseif v:IsA("Accoutrement") then
			mass = mass + v.Handle:GetMass()
        end
    end

    return mass
end

-- in a loop, RenderStep recommended
while true do
	local rootPart = player.Character.HumanoidRootPart
	local force = Instance.new("BodyForce", rootPart)
	local mass = getSummedMass(player.Character)
	-- no need to convert mass to kg but for reference 1kg = 8000 rbx 
	local p = mass * 1.225
	local v = rootPart.Velocity
	-- a cube has a drag coefficient of 1.05 so we will use that
	local Cd = 1.05
	-- when air 'swimming' with top of head oriented with velocity there is an area of about 3 square studs
	local A = 3^2
	
	local DragForce = (.5 * p) * (v.Magnitude^2) * Cd * A
	
	local GravityOffset = Vector3.new(0, mass * workspace.Gravity, 0)
	
	local ActingForce = (v * DragForce) + GravityOffset
	
	force.Force = ActingForce
	game:GetService("RunService").RenderStepped:Wait()
end

However, it simply just poofs the whole character goodbye, and triggers a blackscreen. (as in, the character's parts and accessories/hats just go poof, and a never-ending blackscreen pops up, and the character doesn't recover)

So err....

2 comments

0

merr

0

You can change the speed of the player to mimic that, "air friction" isn't really a thing...

1 answer

Roblox doesn't model drag force due to air resistance.

A simple model is Drag Force = 1/2 p v^2 Cd A where:

p is the fluid density (at sea level air has a density of 1.225 kg/m^3)

v is the velocity magnitude of the object relative to the fluid (just the objects velocity for the sake of keeping things simple)

Cd is the drag coefficient of the object's current cross section

A is the cross section area.

In code this looks a bit like:

-- helper function so we can use a single bodyforce in rootpart 
-- instead of individual bodyforces per part
local function getSummedMass(model)
	local mass = 0 
	for _, v in pairs(model:GetDescendants()) do
		if v:IsA('BasePart') and v.ClassName ~= 'Terrain' then
			mass = mass + v:GetMass()
		end
	end
	
	return mass
end

-- in a loop, RenderStep recommended
local rootPart = character.HumanoidRootPart
local mass = getSummedMass(character)
-- no need to convert mass to kg but for reference 1kg = 8000 rbx 
local p = mass * 1.225
local v = rootPart.Velocity
-- a cube has a drag coefficient of 1.05 so we will use that
local Cd = 1.05
-- when air 'swimming' with top of head oriented with velocity there is an area of about 3 square studs
local A = 3^2

local DragForce = (.5 * p) * (v.Magnitude^2) * Cd * A

local GravityOffset = Vector3.new(0, mass * workspace.Gravity, 0)

local ActingForce = (v * DragForce) + GravityOffset

rootPart.BodyForce.Force = ActingForce

If you're not satisfied with a constant cross section surface area; you can work through how I did it. I fire a bunch of rays from a flat plane, determine if there's an intersection with the object, then sum surface areas of each individual triangle formed by truthy intersections. I do this 180 times to capture a surface area for every angle in 2D.

Hope this helps!

This discussion is preserved as it was. New answers, comments, and votes are closed.