Vector3 values become NAN after period of time

So I got this weird problem and I don’t even know where to start with debugging.

After around 30 seconds of staying still with my mech, it just goes poof then the legs permanently go away.

I can confirm this only happens when the mech stays still because I was able to ride around for 40+ seconds seen in the video

Any thoughts on what’s causing the issue and how I can debug it?

If it helps the animation is done by CFraming the legs client-sided.

5 Likes

Inverse trig functions, division by zero, and some operations on infinity are some of the things that cause NaN’s to appear, the most likely candidate being dividing by zero. What is your code looking like? You are likely getting the Unit of a vector with magnitude 0, but that is my best guess since there is no code you provided.

1 Like

Thanks for the checklist of things I should check in my code which I’ll check on my own eventually.

Edit: NVM Too many scripts to check I’ll do it by myself though if you want to check it’s probably the IK portion of the script which is open source.

https://github.com/datlass/fabrik-ik-motor6d/tree/master/src/ReplicatedStorage/ObjectFolder

The problem is there are a lot more scripts that would need to be checked that does the movement but I’ll need more time and it’s real frustrating.

Edit: NVM I did removed the Tolerance bit to my FabrikSolver IterateOnce Function which does the FABRIK algorithm and the same issue still occurred the whole body dissapears once, however this time only the foot dissapeared, so it’s probably to do with how the foot is updated by CFrames in the LimbChain object.

1 Like

Ok I sorta fixed it by adding a NAN check for the mech’s humanoid root part

--[[
    Then use the LimbChain object to control the motor every heartbeat
    ]]
RunService.RenderStepped:Connect(function(step)

    --NAN Check
    if mechRootPart.Position.X == mechRootPart.Position.X then

    mechController.rotateLegs(step,praetorRig,movingPartsCollision)

    leftLegChain:UpdateMotors()
    rightLegChain:UpdateMotors()

    end
    
end)

Now the legs don’t disappear! :sweat_smile:

However, the vehicle seat which I’m using for input using its throttle values stops functioning and I have to reset it.

So now I’m wondering why it becomes NAN in the first place and disappears?

Currently, I’m suspicious about the server trying to take network ownership, maybe I didn’t fully transfer the server ownership?

So here is the bit of code that does it.

--[[
    This is a server script which gives the player control on sit of the vehicle
    Places the script inside player gui to run like the jeep free model
]] -- Roblox Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- Get this module script
local LocalMechScript = ReplicatedStorage.Source.ClientScripts:WaitForChild(
                            "MechHumanoidControl")

-- Get the local script stored in the module folder
-- Store it here as its constant
local playerLocalMechScript = LocalMechScript:Clone()

local function giveModelToPlayer(model, owner)
    local descendants = model:GetDescendants()

    for index, descendant in pairs(descendants) do
        if descendant:IsA("BasePart") then
            descendant:SetNetworkOwner(owner)
        end
    end
end
-- local humanoid root part

--[[
    Parameter mech pointer refers to the mech the script is in
    accessed from a server cript within the mech model
]]
local function MechController(mechPointer)

    -- Get the mech from the inputted path
    local mech = mechPointer

    local mechSeat = mech:WaitForChild("MechSeat")

    -- Basically give the player the local script on seat and remove
    mechSeat.Changed:Connect(function(property)

        -- If the change is within the occupant value
        if property == "Occupant" then
            --
            if mechSeat.Occupant then
                local player = game.Players:GetPlayerFromCharacter(
                                   mechSeat.Occupant.Parent)
                -- nill check for player for some reason
                if player then
                    print("player has entered the seat")
                    --mechSeat:SetNetworkOwner(player)
                    --mechRootPart:SetNetworkOwner(player)
                    giveModelToPlayer(mech,player)

                    -- Get the local script stored in the module folder
                    -- Gives the local script to the player
                    -- clone again cause it may be garbage collected
                    playerLocalMechScript = LocalMechScript:Clone()
                    playerLocalMechScript.Parent = player.PlayerGui
                    playerLocalMechScript.Disabled = false
                end
            else
                -- reset network ownership
                print("left seat")
                --mechSeat:SetNetworkOwner(nil)
                --mechRootPart:SetNetworkOwner(nil)
                giveModelToPlayer(mech,nil)

                -- If the script still exist then remove it server side
                if playerLocalMechScript then
                    print("Destroying the script on server")
                    playerLocalMechScript:Destroy()
                    playerLocalMechScript = nil
                else
                    print("script already destroyed locally ")
                end
            end
        end
    end)

end

return MechController

Please give me any hints if this has happened to your before or any further hints on what the cause may be.

Maybe it’s a bug with the vehicle seat and I should use UserInputServices or contact action? IDK

Ok I think I found the issue thanks to your idea of tracking down what could possible make it go NAN.

Yeah there’s a bit of code that controls the humanoid movement of my mech and it uses @ThanksRoBama method for smoothing character movement for humanoids so that it they accelerate.

To test it out I intentionally inputted a NAN vector into Humanoid:Move() with this piece of script when the mech’s throttle velocity approaches to zero

local function updateMovement( dt )
    
	if mechHumanoid then

        currentThrottleLevel = lerp(currentThrottleLevel, throttleLevel, math.clamp(dt * moveAcceleration, 0, 1))
        local moveVelocity = currentThrottleLevel*mechRootPart.CFrame.LookVector
        if moveVelocity.Magnitude<0.01 then
--Intentionally input NAN vector into Humanoid:Move()
            moveVelocity = moveVelocity*Vector3.new(0,0,0).Unit
        end
        print(moveVelocity)
        mechHumanoid:Move(moveVelocity)
	end
end	

And yeah the mech IMMEDIATELY DISAPPEARS when I stop it confirming my hypothesis as seen in the video.

Overall it’s a problem with Humanoid:Move() that causes it to disappear for a moment when inputted with a NAN value which then completely destroys the Inverse kinematics solver and makes it all NAN :sweat_smile: :rage:.

Whats causing this value is the asymptotic behavior of the lerp causing extremely low vector 3 values like *10^-24 which eventually causes the vector to become NAN and cause the mech to disappear presumably a floating-point decimal number limit jargon thing.

--The gradual lerp method
        currentThrottleLevel = lerp(currentThrottleLevel, throttleLevel, math.clamp(dt * moveAcceleration, 0, 1))

To fix the problem I will just make the moving velocity (0,0,0) when the magnitude is less than a certain amount to stop this floating-point error I presume from happening.

local function updateMovement( dt )
    
	if mechHumanoid then

        currentThrottleLevel = lerp(currentThrottleLevel, throttleLevel, math.clamp(dt * moveAcceleration, 0, 1))
        local moveVelocity = currentThrottleLevel*mechRootPart.CFrame.LookVector
        if moveVelocity.Magnitude<0.01 then
            moveVelocity = moveVelocity*Vector3.new(0,0,0)
        end
        print(moveVelocity)
        mechHumanoid:Move(moveVelocity)
	end
end	

Overall thanks a lot man I will try my best to warn others of this issue. I’m screaming am happy and angri.

6 Likes

Yep so division by zero ended up being the problem. A unit vector is just a vector divided by its own magnitude. Since Vector3(0, 0, 0) has magnitude 0, by getting the unit of this you are dividing each axis by 0 therefore resulting in NAN, NAN, NAN.

Glad you got the problem fixed and that I could help you find a solution :grin:

2 Likes

Just a small tip, instead of this

if moveVelocity.Magnitude<0.01 then
    moveVelocity = moveVelocity*Vector3.new(0,0,0)
end

you may be able to just do this

if moveVelocity.Magnitude == 0 then
    moveVelocity = Vector3.new(0,0,0)
end
1 Like

Edit: Yep, but I’m putting the blame on the default Roblox Humanoid Object. This is the code I used for additional testing via printing numbers:

local function updateMovement( dt )
    
	if mechHumanoid then

        currentThrottleLevel = lerp(currentThrottleLevel, throttleLevel, math.clamp(dt * moveAcceleration, 0, 1))
        local moveVelocity = currentThrottleLevel*mechRootPart.CFrame.LookVector
        print(moveVelocity)
        mechHumanoid:Move(moveVelocity)
	end
end	

RunService.RenderStepped:Connect(updateMovement)

And this is the result:

As seen as the Vector3 becomes really small close to (0,0,0) and it eventually does becomes partially (0,0,0) in the y axis, that is when the mech dissapears

My theory is that because I’m inputting a very small vector into Humanoid:Move() that it triggers this property within the Humanoid object:

Humanoid.MoveDirection

If you notice it describes the .Unit of the move direction. So yeah when I input a very small number move direction probably there is a split second where the vector 3 gets rounded down to (0,0,0) and the humanoid object being a dumbo tries to measure the .Unit of the move direction it and breaks everything for that split second as well as you described it divides by zero and becomes NAN.

I would report this as a bug but I’m a member and can’t :cry:. Any idea what to do next if needed?

I seem to be having this issue with my mobile players.
At first I just thought my mobile players were confused, or didn’t know how to use the flying script, and for over a year, they complained, that flying (and swimming as it uses the same code) was causing them blink in and out, and crash the code.
Finally someone was able to get a console shot, and showed me that the players HRP, was getting a position of NAN. I had no idea what this meant, and scoured the internet with not much help.
I have experimented for the last few days, trying to narrow this down, and see where the problem is happening. Finally I realized it is happening in the modules of swim and fly and only when either inserting or manipulating the Thrust Force or Body Gyro.
Then I found this post and thought you might be of some help to me.
I am using a modified version of a flying script from the Roblox Gear Pompous, the Cloud - Roblox
(Which by the way, this Gear in a place by itself will unmodified will cause the NAN issue with mobile players)

Looking through the code, I don’t understand the physics of it enough to determine where the problem spot might be, so I thought I might ask you and post the piece of code, to see if you or anyone else might can see where the trouble might be.

function DoPhysics(ElapsedTime)
	
	local Camera = workspace.CurrentCamera
	local CoordinateFrame = Camera.CoordinateFrame
	local Movement = Vector3.new(0, 0, 0)

	if Humanoid.MoveDirection.Magnitude > 0 then
		local localControlVector = CFrame.new(Vector3.new(0,0,0),CoordinateFrame.lookVector*Vector3.new(1,0,1)):vectorToObjectSpace(Humanoid.MoveDirection+Vector3.new(0,.2,0))
		Movement = CoordinateFrame:vectorToWorldSpace(localControlVector.Unit * Speed.Current)
	end

	Momentum = ((Momentum * Inertia) + Movement)
	
	TotalMomentum = math.min(Momentum.Magnitude, Speed.Max)
	local MomentumPercent = (TotalMomentum / Speed.Max)
	
	local Tilt = ((Momentum * Vector3.new(1, 0, 1)).unit:Cross(((LastMomentum * Vector3.new(1, 0, 1)).unit))).y
	if tostring(Tilt) == "-1.#IND" or tostring(Tilt) == "1.#IND" or Tilt == math.huge or Tilt == -math.huge or tostring(0 / 0) == tostring(Tilt) then
		Tilt = 0
	end
	
	local AbsoluteTilt = math.abs(Tilt)
	if AbsoluteTilt > 0.06 or AbsoluteTilt < 0.0001 then
		if math.abs(LastTilt) > 0.0001 then
			Tilt = (LastTilt * 0.96)
		else
			Tilt = 0
		end
	else
		Tilt = ((LastTilt * 0.9) + (Tilt * 0.1))
	end
	LastTilt = Tilt
	
	if TotalMomentum < 0.5 then
		Momentum = Vector3.new(0, 0, 0)
		TotalMomentum = 0
		FlightGyro.CFrame = CFrame.new(Vector3.new(0,0,0),Torso.CFrame.lookVector * Vector3.new(1,0,1)) 
	elseif TotalMomentum < 10 then
		FlightGyro.CFrame = CFrame.new(Vector3.new(0,0,0),Momentum * Vector3.new(1,0,1)) 
	else
		FlightGyro.CFrame = (CFrame.new(Vector3.new(0,0,0), Momentum) * CFrame.Angles(0,0,(Tilt*-20)))
	end
	
	FlightVelocity.Velocity = Momentum --+ Vector3.new(0,JumpMomentum,0) 
	LastMomentum = Momentum
end

Make sure momentum is never equal or too close to 0,0,0. You can add a small vector to do this Vector3.new(0,0.2,0) to make sure the CFrame doesn’t look at itself.

Watch out for this momentum value

Potential Nan error here with .Unit

Encountered this issue creating a fly tool with acceleration. I’ll use your solution, thanks This is the solution I used:

local IsNaN = Velocity.Unit ~= Velocity.Unit
if IsNaN then
    Velocity = Vector3.zero
end
5 Likes
local popo = {
	zeros = {
		["Vector3"] = Vector3.zero;
		["Vector2"] = Vector2.zero;
		["CFrame"] = CFrame.new();
	}
}

function popo:AntiNaN(v)
	local t = typeof(v)
	if t ~= nil then
		local z = popo.zeros[t]
		if (v ~= v) or (v == nil) then
			return z
		end
	end
	return v
end

return popo

wrote this, thought I’d post it incase anyone here wanted it

local nan = require(game.ReplicatedStorage.Modules.NaNWall)
wlk.Force = nan:AntiNaN(moveVelocity)
2 Likes