Problem with changing players plot cframe

Hello! Ive recently been trying to make a system that saves peoples builds. The problem is the person can change their location. What I did was make each plot have the sam x and y but add a z. So for example plot 1 z: 120 plot 2 z 220 etc. I need some help with calculating this change i tried doing this but it sort of worked but not well. Peoples plots got seperated.

The variable “Pos” is the persons new position

tonumber(split[3]) is his old position

‘’’
local maths = nil

			if pos >= tonumber(split[3]) then
				maths = pos-tonumber(split[3])
				if maths >= 99 then
				else
					maths = 0
				end
				print("changes add".. maths)
			elseif pos <= tonumber(split[3]) then
				maths = tonumber(split[3])-pos
				if maths >= 99 then
				else
					maths = 0
				end
				print("changes subtract".. maths)
			elseif pos == tonumber(split[3]) then
				print("no changed need")
				maths = 0
			end
			local position2 = Vector3.new(tonumber(split[1]), tonumber(split[2]), tonumber(split[3])+maths)
			local position1 = Vector3.new(tonumber(split[1]), tonumber(split[2]), tonumber(split[3])-maths)
			
			local orien = Vector3.new(tonumber(split[5]), tonumber(split[6]), tonumber(split[7]))
			
			
			
			if maths >= 0 then
				clone:SetPrimaryPartCFrame(CFrame.new(position2) * CFrame.Angles(math.rad(orien.X), math.rad(orien.Y), math.rad(orien.Z)))
			else
				clone:SetPrimaryPartCFrame(CFrame.new(position1) * CFrame.Angles(math.rad(orien.X), math.rad(orien.Y), math.rad(orien.Z)))
			end

‘’’

Can anyone help me out?

I’m not too sure what you’re asking here. Are you trying to make objects load proportionally to the plot’s CFrame, regardless of the plot’s CFrame?

I think a better approach would be to learn about object and world space. When saving, get each objects’ CFrame in the plot’s object space, use this relative vector/CFrame to save to the data store. Then when loading, translate this back into world space using the new plot’s CFrame. This will handle all cases, such as plot position changes, as well as plot rotation changes.

Yeah but the problem is the plot changes. So in my code I tried to migrate all of the objects to the new position

Yeah that’s why you take each objects’ CFrame, then put it into object space relative to each plot’s CFrame.

Is there anyway you can provide example code I just want to see an example of how it works. If you could that would be great

Yeah,

local plot = path.to.plot -- your plot (a part)

-- for saving,
local plotData = {}

for _, object in plot:GetDescendants() do -- let's say each descendant of the plot is a part
    local relativeCFrame = plot.CFrame:ToObjectSpace(object.CFrame) -- this will be another cframe but will be relative to the plot's cframe
    table.insert(plotData, {object.Name;relativeCFrame:GetComponents()} -- CFrame:GetComponents() will be 12 numbers, there are better ways to optimize how many components are saved but that's up to you to decide
end

-- for loading,
for _, objectInfo in data do
    local objectName = objectInfo[1]
    local objectModel = path.to.models:FindFirstChild(objectName):Clone()
    table.remove(objectInfo, 1) -- we need to remove the object's name since we don't need it for creating the object's new CFrame

    local objectCFrameRelative = CFrame.new(unpack(objectInfo))
    local objectCFrameWorld = plot.CFrame:ToWorldSpace(objectCFrameRelative) -- translate it back to world space

    objectModel.CFrame = objectCFrameWorld
    objectModel.Parent = plot
end

Here is the code I created it just puts everything in the center of the base

split[1] = x

split[2] = y

split[3] = z

and pos posy posx you probably know x y z

‘’'local zDifference = pos - tonumber(split[3])

			print(zDifference)
			
			local xdifference = posx - tonumber(split[1])
			print(xdifference)
			
			local ydifference = posy - tonumber(split[2])

			-- Define a threshold value (in studs) for whether to move the models or not
			local threshold = 99

			-- If the Z-axis difference is greater than or equal to the threshold, move the models
			-- Otherwise, keep them at the same position
			--local maths = zDifference >= threshold and zDifference or 0

			-- Calculate the adjusted positions based on the Z-axis difference
			local position2 = Vector3.new(tonumber(split[1])+xdifference, tonumber(split[2]), tonumber(split[3]) + zDifference)

			-- Define the orientation of the model (you already have it in the split table)
			local orien = Vector3.new(tonumber(split[5]), tonumber(split[6]), tonumber(split[7]))
			clone:SetPrimaryPartCFrame(CFrame.new(position2) * CFrame.Angles(math.rad(orien.X), math.rad(orien.Y), math.rad(orien.Z)))

‘’’

Wait so this code also works even when a player changed the plot?

Sorry didn’t have time to go through this earlier.

You should just be able to call plot.CFrame:ToObjectSpace(otherPart.CFrame), then use that returned cframe’s components to be saved to the datastore. Then use clonedObject.CFrame = plot.CFrame:ToWorldSpace(savedCFrame) to translate it back to world space.

It should yeah.

Ohhh So that means I first get the players plot and get it cframe by using plot.CFrame:ToObjectSpace(partsCFrame). So that moves the parts cframe to the Plots cframe and keeps it rotation and position. Also to do this we have to loop through all of the parts saved. I saved the position and orientation to the datastore so I can just convert that into a cframe. So I loop through the data in the datastore look for the part that got saved set its primarypartCFrame to plot.CFrame:ToWorldSpace(Part.PrimaryPart.CFrame) which in this case I made the primary part for all objects cover the entire model. So that would move the objects to the plot instead of moving the plot to the objects correct?

Ideally you’d use :PivotTo as SetPrimaryPartCFrame is deprecated but yeah that sounds about right.

Wait so here is what I did saving the data:

table.insert(tabletosave, tostring(base.CFrame:ToObjectSpace(CFrame.new(v.MainPart.CFrame.p).X)).." "..tostring(base.CFrame:ToObjectSpace(CFrame.new(v.MainPart.CFrame.p).Y)).." "..tostring(base.CFrame:ToObjectSpace(CFrame.new(v.MainPart.CFrame.p).X)) .." "..v.Name.." "..tostring(v.MainPart.Orientation.X).." "..tostring(v.MainPart.Orientation.Y).." "..tostring(v.MainPart.Orientation.Z))

Here is what I did loading the data:

|||||local orien = Vector3.new(tonumber(split[5]), tonumber(split[6]), tonumber(split[7]))|
|---|---|---|---|---|
||||||
||||||
||||||
|||||local MainFrame = base.CFrame*CFrame.new(position2) * CFrame.Angles(math.rad(orien.X), math.rad(orien.Y), math.rad(orien.Z))|
||||||
|||||--local realframe = base.CFrame:ToObjectSpace(MainFrame)|
||||||
|||||clone:PivotTo(MainFrame)|

ignore those line things. But for some reason I have an error with my code

ServerScriptService.ServerMain:240: invalid argument #2 (CFrame expected, got number)

I dont know whats wrong with this. Can you help out?

I can try my best, which line is producing the error?

Im getting the error from this one: table.insert(tabletosave, tostring(base.CFrame:ToObjectSpace(CFrame.new(v.MainPart.CFrame.p).X))…" “…tostring(base.CFrame:ToObjectSpace(CFrame.new(v.MainPart.CFrame.p).Y))…” “…tostring(base.CFrame:ToObjectSpace(CFrame.new(v.MainPart.CFrame.p).X)) …” “…v.Name…” “…tostring(v.MainPart.Orientation.X)…” “…tostring(v.MainPart.Orientation.Y)…” "…tostring(v.MainPart.Orientation.Z))

Im not sure if the other one works yet but do you think it does. Sorry im not really advanced in coding yet

Oh I see. :ToObjectSpace requires another CFrame so you need to just be doing local toBeSaved = base.CFrame:ToObjectSpace(v.MainPart.CFrame). Then you can use toBeSaved’s components to save it to your datastore

Do you mind providing an example code, im a little confused?

You just do

local base = path.to.base

for _, child in base:GetChildren() do
    local relativeCFrame = base.CFrame:ToObjectSpace(child.CFrame)
    -- do whatever you need with relativeCFrame to save it to the datastore
end

But isnt this what I did but a more simple version.

Since I already used 2 cframes. The plots cframe and the objects cframe

In this line of code:

table.insert(tabletosave, tostring(base.CFrame:ToObjectSpace(CFrame.new(v.MainPart.CFrame.p).X)).." "..tostring(base.CFrame:ToObjectSpace(CFrame.new(v.MainPart.CFrame.p).Y)).." "..tostring(base.CFrame:ToObjectSpace(CFrame.new(v.MainPart.CFrame.p).X)) .." "..v.Name.." "..tostring(v.MainPart.Orientation.X).." "..tostring(v.MainPart.Orientation.Y).." "..tostring(v.MainPart.Orientation.Z))

Not quite the same, you are creating too many CFrames and are trying to get numbers in object space which just won’t work since a single number cannot be translated from world to object and object to world space. An easier way to serialize that data would be to just do

local relativeCFrame = base.CFrame:ToObjectSpace(v.MainPart.CFrame) -- this is a CFrame, it contains both rotational and positional data relative to your plot

local pos = relativeCFrame.Position
local serializedPos = table.concat(pos.X, pos.Y, pos.Z, ' ')
local serializedOrientation = table.concat({relativeCFrame:ToOrientation()})
local serializedCFrame = serializedPos .. ' ' .. serializedOrientation

You should now be left with a string of 6 numbers separated by spaces (serializedCFrame) which you can insert into the data to be saved.

Then for deserializing,

local posX, posY, posZ, orientationX, orientationY, orientationZ = unpack(serializedString:split(' ')

local relativeCFrameFromStore = CFrame.new(posX, posY, posZ) * CFrame.fromOrientation(orientationX, orientationY, orientationZ)

-- now for loading

object:PivotTo(base.CFrame:ToWorldSpace(relativeCFrameFromStore))

Thank you so much! I used a method similar to what you wrote and it saved and loaded! Ill message you if anything else happens but it probably wont. Here is the code I used:

			table.insert(
				tabletosave,
				tostring(base.CFrame:ToObjectSpace(CFrame.new(v.MainPart.CFrame.p)).X) .. " " ..
					tostring(base.CFrame:ToObjectSpace(CFrame.new(v.MainPart.CFrame.p)).Y) .. " " ..
					tostring(base.CFrame:ToObjectSpace(CFrame.new(v.MainPart.CFrame.p)).Z) .. " " ..
					v.Name .. " " ..
					tostring(v.MainPart.Orientation.X) .. " " ..
					tostring(v.MainPart.Orientation.Y) .. " " ..
					tostring(v.MainPart.Orientation.Z)
			)
1 Like