Issues with loading saved rotation values via CFrame

Background

I am currently creating a system where the end user has the ability to fully customize a house they own or rent. The ability to customize involves custom furniture placements on a specific floor plan for a specific house, which would then save upon leaving said server. Should the end user choose to continue playing the game, the data would then load and their house is expected to be where they left it off as.


Completed Aspects

The completed segments of this task include properly sending the server the appropriate position and rotation values for said furniture upon placement. It would then appropriately place the said furniture on the server for everyone to view.

Video of act:

The said rotation (Vector3) of the object, as identified and confirmed by the server is: -0.49999997, 0, 0.866025329. The function used to place the object appropriately for the rotation is: object:GetPrimaryPartCFrame().toAxisAngle(CFrame.new(Rotation[1],Rotation[2],Rotation[3]),0), granted object is considered the furniture model.

Upon leaving the server, the datastore confirms that it has the following values: -0.49999997, 0, 0.866025329. This is printed in the console for verification.

When the end user rejoins and loads in their stored furniture, the server identifies the saved value of -0.49999997, 0, 0.866025329 but loads in the value of -0, -0, -1, rotating the furniture into it’s original rotation stored on the server.

The function that handles loading the stored data is as follows: newSet:GetPrimaryPartCFrame().toAxisAngle(CFrame.new(Position[2][1],Position[2][2],Position[2][3]),0). Do note that the indicated values within CFrame.new are the same number values that was stored in the datastore and loaded, confirmed by the console.

image


The Obstacle

As shown in the completed aspect, the loading of the end user’s furniture placement is not rotating as intended to do so. Is there a mistake in regards to what I am doing with CFrame?


The Full Code

Placement from Client to Server

	local object = nil
	local FurnitureName = tab[2]
	local Position = tab[3]
	local Rotation = tab[4]
	
	object = FurnitureObjects:WaitForChild(FurnitureName):Clone()
	object.PrimaryPart = object:WaitForChild("Center")
	CanCollide(object,false)
	object:MakeJoints()
	
	object:SetPrimaryPartCFrame(Position)
	
	local newRotationValue = Instance.new("Vector3Value")
	newRotationValue.Name = "RotationValueVECTOR3"
	newRotationValue.Value = Vector3.new(Rotation[1],Rotation[2],Rotation[3])
	newRotationValue.Parent = object
	
	object:GetPrimaryPartCFrame().toAxisAngle(CFrame.new(Rotation[1],Rotation[2],Rotation[3]),0)
	print("Placed:")
	warn(Vector3.new(Rotation[1],Rotation[2],Rotation[3]))
	
	object.Parent = workspace.BuyableHouses.Plot.PlacedObjects
	
	-- handle ceiling limit (needs to be fixed)
	if (object.Center.Position.Y - object.Parent.Parent:FindFirstChild("FloorPlan").Position.Y) >= object.Parent.Parent.Configuration.Ceiling.Value then
		object:Destroy()
	end
	
	CanCollide(object,true)

Save from Server to Datastore

function serializeFurniture(plot)
local data = {}
local FloorPlan = plot.Parent:FindFirstChild("FloorPlan"):Clone()
local newModel = Instance.new("Model")
FloorPlan.Parent = newModel
newModel.PrimaryPart = FloorPlan
for i,v in pairs(plot:GetChildren()) do
	local newFurniture = v:Clone()
	v.Parent = newModel
	v.PrimaryPart = v:WaitForChild("Center")
end

local dataCount = 0
for i,v in pairs(newModel:GetChildren()) do
	if v:IsA("Model") then
		dataCount = dataCount + 1
		local PrimaryPartCFrame = v:GetPrimaryPartCFrame()
		local CFrameSegment = {PrimaryPartCFrame.p,PrimaryPartCFrame.x,PrimaryPartCFrame.y,PrimaryPartCFrame.z}
		
		--local objectRotationValue = v:WaitForChild("RotationValueVECTOR3")
		local objectRotationValue = v:FindFirstChild("RotationValueVECTOR3")
		print("Saved: ")
		warn(objectRotationValue.Value)
		
		local RotationValues = {
			objectRotationValue.Value.X,
			objectRotationValue.Value.Y,
			objectRotationValue.Value.Z
		}
		local Position = {CFrameSegment,RotationValues}
		--local Rotation = v:GetPrimaryPartCFrame().lookVector
		data[dataCount] = {v.Name,Position}
	end
end

return data

end

function module:SaveHouse(player,plot)
Datastore:SetAsync(player.UserId,game:GetService(“HttpService”):JSONEncode(serializeFurniture(plot)))
print(“House Saved!”)
end

Load from Datastore to Server

function module:LoadHouse(player,houseFloor)
local previouslySavedHouse = Datastore:GetAsync(player.UserId)

if previouslySavedHouse == nil then
	print("House did not exist!")
	return nil
else
	previouslySavedHouse = game:GetService("HttpService"):JSONDecode(previouslySavedHouse)
	local newModel = Instance.new("Model")
	local newFloorPlan = workspace.BuyableHouses.Plot.FloorPlan:Clone()
	newFloorPlan.Transparency = 1
	newFloorPlan.Parent = newModel
	newModel.PrimaryPart = newFloorPlan
	newModel.Parent = workspace.BuyableHouses.Plot
	
	for i,v in pairs(previouslySavedHouse) do
		local newSet = game.ReplicatedStorage.HouseConfiguration.Furniture:FindFirstChild(v[1]):Clone()
		
		newSet.PrimaryPart = newSet.Center
		newSet.Parent = newModel
		
		CanCollide(newSet,false)
		newSet:MakeJoints()
		local Position = v[2]
		local CFrameInfo = CFrame.new(
			--Position[1][1],
			Position[1][2],
			Position[1][3],
			Position[1][4]
			--Vector3.new(Position[2].lookVector[1],Position[2].lookVector[2],Position[2].lookVector[3]),
			--Vector3.new(Position[2].rightVector[1],Position[2].rightVector[2],Position[2].rightVector[3]),
			--Vector3.new(Position[2].upVector[1],Position[2].upVector[2],Position[2].upVector[3])
		)
		newSet:SetPrimaryPartCFrame(CFrameInfo)
		
		--print("Rotation: "..Rotation)
		
		-- HELPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP ROTATION DOESNT WORK
		--[[
			NOTE TO SELF
			
			rotation values return correct, by why isnt it rotation xd
		--]]
		print("Loaded: ")
		warn(Vector3.new(Position[2][1],Position[2][2],Position[2][3]))
		--newSet.PrimaryPart.Orientation = Vector3.new(Position[2][1],Position[2][2],Position[2][3])
		newSet:GetPrimaryPartCFrame().toAxisAngle(CFrame.new(Position[2][1],Position[2][2],Position[2][3]),0)
		
		warn("placed: ")
		warn(newSet:GetPrimaryPartCFrame().lookVector)
		
		local newRotationValue = Instance.new("Vector3Value")
		newRotationValue.Name = "RotationValueVECTOR3"
		newRotationValue.Value = Vector3.new(Position[2][1],Position[2][2],Position[2][3])
		newRotationValue.Parent = newSet
		
		
		CanCollide(newSet,true)
		--newSet.Parent = workspace.BuyableHouses.Plot.PlacedObjects
	end
	
	print("House Loaded!")
end

end


Thanks in advance! I apologize if there are similar threads, I have tried researching and have completed busted out. I’ve stopped this specific area for about several months and put it off to do later since I hit this specific obstacle.

4 Likes

I don’t completely grok the code you posted, but there are some issues that I can see right away. In this snippet:

Most of the problems seem to come from a lack of understanding about how the different CFrame constructors and methods work. You can read up on the details here.

you’re using the CFrame.new(x, y, z) constructor, which takes an X, Y and Z value to create a CFrame with a position component of (X, Y, Z) and no rotation components (which are instead initialized to the default rotation).

You then pass that as an argument to the CFrame.ToAxisAngle() method, which returns the rotational components (as a Vector3 and a number, representing an axis of rotation and a magnitude of rotation) of whichever CFrame value it was called on. Since you’re calling it on a CFrame which has the default rotation, you’ll always get Vector3.new(1, 0, 0), 0 from that CFrame.ToAxisAngle() call. This (among other things) explains why the bed just has the default rotation instead of the one you loaded.

Also, you’re referring to the CFrame.ToAxisAngle method by indexing a CFrame value that you got from object:GetPrimaryPartCFrame(), that’s Get PrimaryPartCFrame, in case you made a typo. It works, but it doesn’t seem to be what you’d really want, since you’re just computing a default rotation and never setting any variable or property to the value you computed. This is another reason that you get the default rotation for the bed, you’re never actually updating the model’s rotation. It’s also a pretty confusing way of doing it, even if it is what you want.

The snippet could be simplified to this:

CFrame.new( Rotation[1], Rotation[2], Rotation[3] ):ToAxisAngle(0)

Since ToAxisAngle doesn’t expect any arguments, it can be further simplified to

CFrame.new( Rotation[1], Rotation[2], Rotation[3] ):ToAxisAngle()

and since the newly created CFrame will always have the default rotation, it ends up doing the exact same as

Vector3.new(1, 0, 0), 0

That last snippet isn’t valid Lua though, because just saying a constant (i.e. 0 in this case) without assigning it to anything or passing it as an argument to a function doesn’t really make any sense. It’s functionally the same though, because you just throw away the values that you computed.

Here’s a snippet that might be what you actually wanted:

object:SetPrimaryPartCFrame(
    object:GetPrimaryPartCFrame() * CFrame.Angles(Rotation[1],Rotation[2],Rotation[3])
)

This assumes that Rotation 1-3 holds the pitch, yaw and roll parts of the rotation you want. If you actually did want it to be as an axis angle, you’d need to pass the X, Y, and Z parts of the axis, as well as the magnitude of rotation, which you don’t seem to have in Rotation.

Hope this helps, and wasn’t too ranty :slight_smile: