CFrame(X,X) producing NaNs

Yes, Yes, I know I quit and I shouldn’t be posting this, but eh… there’s no way I could possibly make anyone upset if all I do is file a bug report, might as well be a little useful, so I’ll drop this here and be gone again

print(CFrame.new(Vector3.new(100,100,100),Vector3.new(100,100,100)))
–> 100, 100, 100, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN

This produces NaNs in the rotation matrix, would like a fix (default to identity matrix rotation), likely it’s an issue with bad math in how you handle zero vectors

3 Likes

Why would you want to have a CFrame point at itself?

local ,,_,a = MyStupidSelf_PointingCFrame:components()
if not (a < 0) and not (a >= 0) then
–nan
end

You don’t, but it often happens by accident and because you don’t expect this behavior, it can be game breaking

In my particular case, when a player joins my game, they get a table of units pulled immediately from the server, then, if the unit was moving, there’s a slight chance the new position of the unit arrived in the pull, before the movement packet arrived, causing the unit’s CFrame to be set to looking at it’s current position, turning it’s rotation matrix into all NaNs, making the next movement of the unit push it into a position infinity away, this bug has taken me weeks to dissect because all that seems to happen is the units don’t show up if you join the game, or respawn, I spent tons of time reviewing my code, checking that it wasn’t parenting the unit to nil, seeing why the units were gone, etc, and it was such a rare issue it was almost impossible to repro in a controlled environment, which is tons of dev time, and much grief spent over a simple thing like an improperly written CFrame function

Although, that being said, I have already patched this issue in my game, but I’m pretty sure someone else will run into this problem sooner or later, and have the same headache as me (although hopefully not in nearly as complex of a system as mine, having to sift through 2k lines of intricate code for a rare bug that on the surface appears to be completely different)

4 Likes

This shouldn’t be fixed, it’s a garbage-in-garbage out scenario. If you tell CFrame.new to do something that doesn’t make sense it shouldn’t try to hide the problem and give you back a reasonable output.

The documentation should probably make it more clear that you have to be careful to think of this case that you’re talking about when using the function (maybe the engine should even output a warning in that case?), but I don’t think that it should be “fixed” to try to produce some valid (but probably not what the coder wanted) output.

3 Likes

This bug report was nine years ago but I feel it’d be helpful for anyone else who experiences this in the future. Not sure if it’s something that should be changed in engine, or be clarified through documentation that there are edge cases that could cause this issue to decimate physical parts of your game which rely on lookAt / CFrame.new(x, y).

I have an NPC which looks at nearby players, and the player is spawned in on top of them, shortly before they start wandering around. I have an AlignOrientation in this NPC which sets its CFrame to lookAt the nearest player. It would multiply each vector in CFrame.lookAt by Vector3.new(1, 0, 1), so that the NPC won’t have their body look up or down.

While testing, at some point I noticed that all of the NPC’s limbs and parts would disappear from both the client and the server - I initially thought this had something to do with streaming by toggling it off where it didn’t seem to happen, and the micro profiler would have streaming labels causing lag spikes, as well as large empty gaps where it wouldn’t show anything. Turns out that it’s because both Vector3s initially equal each other, it causes the NPC’s AlignOrientation to try to align the body to NaN, which might’ve caused the world destroy height to remove every part in the model, even if the Workspace property is set to 0/0 which sorta removes the destroy height.


Here’s a simple fix/workaround for AlignOrientation if you’re doing something like what I was doing, where either all or none of the rotation vectors will become NaN. I don’t use the CFrame position for the AlignOrientation, but I decided to include it in case if someone wants it reserved. Note that this won’t check every value in the matrix.

local function NormalizeAngles(CF: CFrame): CFrame
	local R00 = select(4, CF:GetComponents())
	if R00 == R00 then
		return CF
	else
		local identity = CFrame.identity
		return CFrame.fromMatrix(CF.Position, identity.XVector, identity.YVector, identity.ZVector)
	end
end
2 Likes

Bump. Would really appreciate a warning from the engine when a CFrame value has become NaN.

1 Like

Bump. I really need fixes to this.

I got a change rolling to have the CFrame.lookAt and CFrame.new return an unrotated CFrame in that case instead. Expect a change to ship for this a few weeks from now.

Though understand that for ideal behavior you probably still need to handle the a = b case explicitly in most cases (e.g.: Remembering and retaining the orientation the thing last had before a became equal to b).

6 Likes