Issues with turning object orientation into object space

I am building a tycoon game, and the game has 6 plots. 3 of these plots are rotated by 180 degrees comparing to the other 3

The problem is, if you place an object on plot A, load into plot B,the object will flip by 180 degrees, but this will fix by reloading into plot B again.

However, when loaded into plot A again (after loading into B twice), the object will be flipped by 180 degrees, with the only way to reverse this being loading into plot B and into plot A again.

This is what should happen in theory:

but this is what happens in reality:

here is the code used for saving and loading objects

-- saving
for i, obj in ipairs(plot.BuildMat.itemHolder:GetChildren()) do -- gets all objects from the itemHolder folder, located inside the plot
	table.insert(data, {
		["name"] = obj.Name,
		["transform"] = {
			["x"] = plot.BuildMat.CFrame:ToObjectSpace(CFrame.new(obj.PrimaryPart.CFrame.p)).X;
			["y"] = plot.BuildMat.CFrame:ToObjectSpace(CFrame.new(obj.PrimaryPart.CFrame.p)).Y;
			["z"] = plot.BuildMat.CFrame:ToObjectSpace(CFrame.new(obj.PrimaryPart.CFrame.p)).Z;
			["r"] = obj.PrimaryPart.Orientation.Y
                                --saves the object position, and y rotation
		}
	})
end
		
-- loading
for i, saved in ipairs(data) do -- get the objects from the DataStore
	local loadedModel:Model = replicatedStorage.models:FindFirstChild(saved.name):Clone() -- most models dont differentiate from one another, so the model gets pulled from replicatedStorage.
	
	if loadedModel then
		loadedModel:PivotTo(plot.BuildMat.CFrame * CFrame.new(saved.transform.x, saved.transform.y, saved.transform.z)  * CFrame.Angles(0, math.rad(saved.transform.r), 0))
		loadedModel.Parent = plot.BuildMat.itemHolder
	else
		return
	end
end

--plot.BuildMat is the plot itself. contains the itemHolder folder
--code has been simplified, all other things function perfectly fine.

the above code perfectly loads in the object position, however I do not quite understand how to save rotation into ObjectSpace.

1 Like

To get the rotation of a CFrame relative to another, it would look something like this:

local cframe = relativeobject:ToObjectSpace(otherobject).Rotation

applying this edit and reconnecting from plot A to plot A makes all objects reset their rotation to 0 degrees.

replaced:

["r"] = obj.PrimaryPart.Orientation.Y

with:

["r"] = plot.BuildMat.CFrame:ToObjectSpace(obj.PrimaryPart.CFrame).Rotation.Y

I’m assuming you’re wanting to save the objects relative to the baseplate’s orientation? In that case, just save the offset between the baseplate’s orientation and the object’s orientation (same with position). Then on load, just set the object’s position and orientation relative to the baseplate’s position and orientation.

Although generally, with a grid placement system like The Sims, you’ll only need to save the grid position and object rotation without having to worry about any offsets as that can be calculated automatically.

applying what i interpreted from your response leads to all objects resetting their rotation to 0, even from A to A (refer to diagram)

old code:

--saving
["r"] = obj.PrimaryPart.Orientation.Y

--loading
loadedModel:PivotTo(plot.BuildMat.CFrame * CFrame.new(saved.transform.x, saved.transform.y, saved.transform.z)  * CFrame.Angles(0, math.rad(saved.transform.r), 0))

new code:

--saving
["r"] = math.rad(plot.BuildMat.Orientation.Y) - math.rad(obj.PrimaryPart.Orientation.Y)

--loading
local newRotation = math.rad(plot.BuildMat.Orientation.Y) + math.rad(saved.transform.r)
loadedModel:PivotTo(plot.BuildMat.CFrame * CFrame.new(saved.transform.x, saved.transform.y, saved.transform.z)  * CFrame.Angles(0, newRotation, 0))

so far, all my attempts to turn the rotation into ObjectSpace resulted in rotation reset. I am unsure of any other ways to fix this issue, but maybe ObjectSpace is not the best solution

also, the rotation is more similar to chillthrill709 - Build A Boat For Treasure, mixed with Welcome to Bloxburg, rather than The Sims. This is due to there being multiple plots, as its a multiplayer game.

Bloxburg was directly inspired by The Sims. My point was that type of building / object placement system.

You appear to be using math.rad a bit excessively. You only need to use math.rad when setting CFrame.Angles().

I’d give a better example, but I’m about to get off as it’s very late. If I remember to and you still need help, I’ll throw something together to better show what I’m talking about.

1 Like

it is late for me too.

I would really appreciate this! Thank you!

Okay so I threw this together. I’m still waking up so this is pretty garbage (plus you only need the one rotation axis that’s actually relevant, but here’s sort of what I meant):

Old Code (Ignore)
local httpService = game:GetService('HttpService')
local replicatedStorage = game:GetService('ReplicatedStorage')
local objects = replicatedStorage:WaitForChild('Housing Objects')
local plots = workspace:WaitForChild('Plots')
local plot1 = plots:WaitForChild('Plot 1')
local plot2 = plots:WaitForChild('Plot 2')

function OrientationToCFrame(orientation)
	return CFrame.Angles(math.rad(orientation.X),math.rad(orientation.Y),math.rad(orientation.Z))
end

local ConvertVector3 = {}
function ConvertVector3:ToArray(vector3)
	return {vector3.X,vector3.Y,vector3.Z}
end
function ConvertVector3:ToVector3(array)
	return Vector3.new(array[1],array[2],array[3])
end

function SavePlot(plot)
	local data = {}
	data.Objects = {}
	data.Orientation = ConvertVector3:ToArray(plot.Baseplate.Orientation)
	for _,object in plot.Models:GetChildren() do
		local temp = {}
		temp.Name = object.Name
		temp.Position = ConvertVector3:ToArray(object.PrimaryPart.Position-plot.Baseplate.Position)
		temp.Orientation = ConvertVector3:ToArray(object.PrimaryPart.Orientation-plot.Baseplate.Orientation)
		table.insert(data.Objects,temp)
	end
	return httpService:JSONEncode(data)
end

function LoadPlot(plot,data)
	data = httpService:JSONDecode(data)
	for _,object in data.Objects do
		local temp = objects:FindFirstChild(object.Name)
		if temp then
			local model = temp:Clone()
			model:PivotTo(plot.Baseplate.CFrame*CFrame.new(ConvertVector3:ToVector3(object.Position))*OrientationToCFrame(ConvertVector3:ToVector3(object.Orientation)))
			model.Parent = plot.Models
			model = nil
			temp = nil
		end
	end
end

task.wait(3) --Just to let everything load for the test

LoadPlot(plot2,SavePlot(plot1))

​
​

You’ll also want to set a PrimaryPart as the “Hitbox” that you get the position/orientation from.

I believe by making a plotcframe, you can use a module, and use Object:PivotTo(plotcframe*ObjectprimaryCframe). for example I do not know if i got it the cframes flipped inside hte brackets. but heres an example, for the object to be in the center of the plot its cframe would have to be like (0,0,0) and whenever the plotcframe is rotated so will the Object, because its primary cframe is the same.

And i assume that in order to save it in between games, you just save the httpService:JSONEncode(data), and then get it from the data store to load?

Yes, that’s all there is to it. I used json to save all of the data as a string. But again, you can just save the Y axis (I think that’s the relevant rotation) for all of the offset stuff instead of the full Vector3s.

I placed my objects in the front-left corner of plot A.

when I reloaded into plot A (see below, step 1), everything works fine.
when I reloaded from A to B (step 2), it was fine at first, but then when I reloaded again from B to B (step 3), the objects that were in the front-left, are now in the back-right corner. reloading again (step 4) fixes this, but reloading for the 3rd (step 3 again) time puts the objects into back-right again. This only appears to be:

image

my apologies for making this the way it is, I find it easier to explain like this instead of studio screenshots

also forgot to mention that now, at least the rotation is generally the way its supposed to be, but the objects themselves get rotated as if the plot was 180 rotated (it wasnt)

I can provide my new code if needed.

Thank you MightyDantheman, i used some of your cde, with some of mine, and there are no problems now…

for all the people in the future who read this, here is what the new saving and loading code looks like
important stuff:

local players = game:GetService("Players")
local replicatedStorage = game:GetService("ReplicatedStorage")
local HttpService = game:GetService("HttpService")

local plotManager = require(script.Parent.ServerModules.PlotManager) - this assigns plots

local dataStoreService = game:GetService("DataStoreService")
local dataStore = dataStoreService:GetDataStore("BuildingSystemStore") - saves

local tries = 3
local dataloaded = nil

local objects = replicatedStorage:WaitForChild('models') - where the object template is saved

function OrientationToCFrame(orientation)
	return CFrame.Angles(math.rad(orientation.X),math.rad(orientation.Y),math.rad(orientation.Z))
end

local ConvertVector3 = {}
function ConvertVector3:ToArray(vector3)
	return {vector3.X,vector3.Y,vector3.Z}
end

function ConvertVector3:ToVector3(array)
	return Vector3.new(array[1],array[2],array[3])
end

saving function:

function SavePlot(plot)
	local data = {}
	data.Objs = {}
	data.Rotate = ConvertVector3:ToArray(plot.BuildMat.Orientation)
	for _,object in plot.BuildMat.itemHolder:GetChildren() do
		local temp = {}
		temp.Name = object.Name
		temp.Position = ConvertVector3:ToArray(Vector3.new(plot.BuildMat.CFrame:ToObjectSpace(CFrame.new(object.PrimaryPart.CFrame.p)).X, plot.BuildMat.CFrame:ToObjectSpace(CFrame.new(object.PrimaryPart.CFrame.p)).Y, plot.BuildMat.CFrame:ToObjectSpace(CFrame.new(object.PrimaryPart.CFrame.p)).Z))
		temp.Orientation = ConvertVector3:ToArray(object.PrimaryPart.Orientation-plot.BuildMat.Orientation)
		table.insert(data.Objs,temp)
	end
	return HttpService:JSONEncode(data)
end

load function:

function LoadPlot(plot,data)
	if data == nil then
		data = {}
	end
	data = HttpService:JSONDecode(data)
	for _,object in data.Objs do
		local temp = objects:FindFirstChild(object.Name)
		if temp then
			local model = temp:Clone()
			model:PivotTo(plot.BuildMat.CFrame*CFrame.new(ConvertVector3:ToVector3(object.Position))*OrientationToCFrame(ConvertVector3:ToVector3(object.Orientation)))
			model.Parent = plot.BuildMat.itemHolder
			model = nil
			temp = nil
		end
	end
end

the above functions have been provided by MightyDantheman

modified a little bit by me

To save:

local ToSet = SavePlot(plot) -- i have a module script that assigns plots when a user joins, and when requested, provides the path to that plot

local success, err
		
repeat
	success, err = pcall(function()
		dataStore:SetAsync(player.UserId, toSave)
	end)
until success -- dont reccomend, as it might make too many requests. Implement your own feature here

to load:

local data

local success, err

repeat
	success, err = pcall(function()
		data = dataStore:GetAsync(player.UserId)
	end)
until success

if data then
	LoadPlot(plot, data) - again, specify your own plot info and data.
end

hope this helps anyone else thats making a tycoon game!

1 Like

Sorry for the super late response. If your solution works, that’s great. I kinda forgot to apply the orientation from the baseplate and messed up the order a bit, but this is my updated code:

local httpService = game:GetService('HttpService')
local replicatedStorage = game:GetService('ReplicatedStorage')
local objects = replicatedStorage:WaitForChild('Housing Objects')
local plots = workspace:WaitForChild('Plots')
local plot1 = plots:WaitForChild('Plot 1')
local plot2 = plots:WaitForChild('Plot 2')
local plot3 = plots:WaitForChild('Plot 3')

function OrientationToCFrame(orientation)
	return CFrame.Angles(math.rad(orientation.X),math.rad(orientation.Y),math.rad(orientation.Z))
end

local ConvertVector3 = {}
function ConvertVector3:ToArray(vector3)
	return {vector3.X,vector3.Y,vector3.Z}
end
function ConvertVector3:ToVector3(array)
	return Vector3.new(array[1],array[2],array[3])
end

function SavePlot(plot)
	local data = {}
	data.Objects = {}
	data.Orientation = ConvertVector3:ToArray(plot.Baseplate.Orientation)
	for _,object in plot.Models:GetChildren() do
		local temp = {}
		temp.Name = object.Name
		temp.Position = ConvertVector3:ToArray(object.PrimaryPart.Position-plot.Baseplate.Position)
		temp.Orientation = ConvertVector3:ToArray(object.PrimaryPart.Orientation)
		table.insert(data.Objects,temp)
	end
	return httpService:JSONEncode(data)
end

function LoadPlot(plot,data)
	data = httpService:JSONDecode(data)
	for _,object in data.Objects do
		local temp = objects:FindFirstChild(object.Name)
		if temp then
			local model = temp:Clone()
			model:PivotTo(plot.Baseplate.CFrame*OrientationToCFrame(ConvertVector3:ToVector3(data.Orientation))*CFrame.new(ConvertVector3:ToVector3(object.Position))*OrientationToCFrame(ConvertVector3:ToVector3(object.Orientation)))
			model.Parent = plot.Models
			model = nil
			temp = nil
		end
	end
end

task.wait(3)

local plot1Data = SavePlot(plot1)
LoadPlot(plot3,plot1Data)
local plot3Data = SavePlot(plot3)
plot1.Models:ClearAllChildren()
LoadPlot(plot1,plot3Data)
LoadPlot(plot2,plot3Data)

image

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.