Help in saving positions

  1. What do you want to achieve?
    In my game, players can add furniture inside their ships. The ships move with the furniture inside them smoothly (without any performance issues)

  2. What is the issue?
    The Issue comes in saving a furniture’s Y-axis. The Y axis is either a few studs bellow what is should be, or a few studs above. Each furniture piece has a (0.1, 0.1, 0.1) sized invisible part at the very lowest part of the furniture piece. It is the Primary part of all furniture models. This part is used to gather the X, Y, Z, and Y rotation of the furniture. This goes well, except for the Y-axis positioning.

  3. What solutions have you tried so far?

  • I tried saving the positions with the ship’s Orientation being 0,0,0 with no difference
  • I tried using Ship.Hull:ToObjectSpace(CFrame.new(Furniture.PrimaryPart.Position))) instead of Ship.Hull:ToObjectSpace(CFrame.new(Furniture.PrimaryPart.CFrame))) with again, no solution.
  • Furniture is loaded in by cloning a furniture item with the same name in ReplicatedStorage, and adding the saved positions with the ship’s Hull

Things to note:

  • the furniture is not in the workspace if the player is not inside the ship
  • The Furniture is welded to the ship
  • The ship’s network owner is the server unless if someone is driving the ship (still happens no matter what the network owner is)

here is a chunk of code on the server side, which saves furniture positions. Again, this saves the X and Z axis normally. But has issues with saving the Y axis

game.ReplicatedStorage.Remotes.Functions.PlaceFurniture.OnServerEvent:Connect(function(player, name, pos, oren, furnCFrame)
	if player.DataStoreFolder.InteriorCheck.Value == true and player.DataStoreFolder.PlayerFurniture:FindFirstChild(player.DataStoreFolder.PlayerInfoFolder.ShipSpawned.Value.ShipClass.Value).SlotAt.Value < 60 then
		local Hull = player.DataStoreFolder.PlayerInfoFolder.ShipSpawned.Value.Hull
		if player.DataStoreFolder.PlayerFurniture:FindFirstChild(Hull.Parent.ShipClass.Value).SlotAt.Value <= 60 then -- checks if a player has an open slot value (max 60 furniture per ship) and if the player is inside their ship (so they cant place items outside their ships)
			player.DataStoreFolder.PlayerFurniture:FindFirstChild(name).Value = player.DataStoreFolder.PlayerFurniture:FindFirstChild(name).Value -1
			local furniture = game.ReplicatedStorage.Furniture:FindFirstChild(name):Clone()
			furniture.Parent = player.DataStoreFolder.PlayerInfoFolder.ShipSpawned.Value.FurnitureValue.Value
			furniture.Highlight:Destroy()
			print(furniture.Name)	
			furniture.rot.Parent = workspace
			workspace.rot.Orientation = oren
			furniture:SetPrimaryPartCFrame(workspace.rot.CFrame)
			furniture:SetPrimaryPartCFrame(CFrame.new(pos))
			
			local found = false	
			
			local Saved = player.DataStoreFolder.PlayerFurniture:FindFirstChildWhichIsA("StringValue")
			
			
			
			local weld = Instance.new("Weld")
			weld.Part0 = player.DataStoreFolder.PlayerInfoFolder.ShipSpawned.Value.Hull  
			weld.Part1 = furniture:FindFirstChild(name)
			weld.Parent = furniture:FindFirstChild(name)
			
			weld.C0 = Hull.CFrame:toObjectSpace(furniture:FindFirstChild(name).CFrame) * CFrame.Angles(0,0,0)
			weld.C1 = CFrame.new()
			
			Hull.Anchored = false
			
			player.DataStoreFolder.PlayerFurniture:FindFirstChild(player.DataStoreFolder.PlayerInfoFolder.ShipSpawned.Value.ShipClass.Value).SlotAt.Value = player.DataStoreFolder.PlayerFurniture:FindFirstChild(player.DataStoreFolder.PlayerInfoFolder.ShipSpawned.Value.ShipClass.Value).SlotAt.Value+1
			local Serial = Instance.new("ObjectValue") -- has an object value representing the slot it takes, so when the furniture is deleted, it can be referenced from the furniture
			Serial.Name = "SerialInstance"
			Serial.Parent = furniture
			Serial.Value = player.DataStoreFolder.PlayerFurniture:FindFirstChild(player.DataStoreFolder.PlayerInfoFolder.ShipSpawned.Value.ShipClass.Value):FindFirstChild("Slot"..player.DataStoreFolder.PlayerFurniture:FindFirstChild(player.DataStoreFolder.PlayerInfoFolder.ShipSpawned.Value.ShipClass.Value).SlotAt.Value)
			
			for i, V in pairs(furniture:GetChildren()) do
				if V:IsA("MeshPart") or V:IsA("Part") or V:IsA("WedgePart") or V:IsA("UnionOperation") then
					V.Anchored = false
				end
			end	
			local org
			org = Hull.Orientation
			
			Hull.Orientation = Vector3.new(0,180,0) -- saves furniture with this orientation
			for i, V in pairs(player.DataStoreFolder.PlayerFurniture:FindFirstChild(Hull.Parent.ShipClass.Value):GetChildren()) do -- finds an open slot in the player's furniture folder to save data
				if V:IsA("StringValue") then
					if V.Value == "" and found == false then
						found = true	
						local Saved = V -- Saved is the stringvalue which is a furniture slot. PositionX, PositionY, PositionZ are it's children and are what the positions are relative to the ship
						local g = Hull.CFrame:ToObjectSpace(CFrame.new(furniture.pos.Position))
						
						
						Saved.PositionX.Value = -g.X 
						Saved.PositionY.Value = g.Y 
						Saved.PositionZ.Value = g.Z 
						Saved.Rotation.Value = workspace.rot.Orientation.Y
						Saved.Value = name
						Saved.Parent = player.DataStoreFolder.PlayerFurniture:FindFirstChild(Hull.Parent.ShipClass.Value)
					end	
				end
			end
			Hull.Orientation = org
		end
		workspace.rot:Destroy()
	end
end)

here is some footage: https://youtu.be/wPyoD4G8zLc

Alright, to start, am I correct your firing a remote event every frame? If you are, this isn’t good, I would recommend creating values inside the player, with the name, position, orientation, cframe, etc.

1 Like

Oh, no. There is a client side positioning thingy, and when the player clicks (or long tap on mobile) it fires the remote event once with the position of the furniture, type, and the rotation. The server script clones that specific type of furniture from replicatedstorage, deletes the neon highlight part, and places the furniture exactly what it was like in the client. Then it saves the positions accordingly. The issue comes when saving the Y axis

here is the local script (keep in mind I am 95% sure its not causing the issue, since the positions it sends in the remote event when positioned by the server sciript is 100% where it was in the client)

			Stepped = RenderStepped:Connect(function()
				local mouseRay = mouse.UnitRay
				local CastRay = Ray.new(mouseRay.Origin, mouseRay.Direction*30)
				local hit, position = workspace:FindPartOnRayWithIgnoreList(CastRay, ignorelist)
				if hit then									
					if  GUI.SideFrame.SnapBt.Snapped.Value == true then
						local snapRay = Ray.new(position, Vector3.new(0,-50,0))
						local snaphit, SnapPos = workspace:FindPartOnRayWithIgnoreList(snapRay, ignorelist)
						if SnapPos then
							local nwps = Vector3.new((math.fmod(SnapPos.X, 1)- SnapPos.X),SnapPos.Y, (math.fmod(SnapPos.Z, 1)- SnapPos.Z))
							ClientFurniture:SetPrimaryPartCFrame(CFrame.new(nwps.X, nwps.Y+ClientFurniture.YOffset.Value, nwps.Y))
							SendPosition = nwps
						end
					else
						local NormalRay = Ray.new(Vector3.new(position.X, position.Y+3, position.Z), Vector3.new(0,-10,0))
						local instanceHit, NewPosition = workspace:FindPartOnRayWithIgnoreList(NormalRay, ignorelist)
						ClientFurniture:SetPrimaryPartCFrame(CFrame.new(NewPosition.X, NewPosition.Y+ClientFurniture.YOffset.Value, NewPosition.Z))
						SendPosition = NewPosition
						SendCFrame = ClientFurniture.PrimaryPart.CFrame
						
					end
					if (ClientFurniture.pos.Position.Y - Hull.Hull.Position.Y) > Hull.ShipClass.MaxY.Value or (ClientFurniture.pos.Position.X - Hull.Hull.Position.X) > 225 or (ClientFurniture.pos.Position.Z - Hull.Hull.Position.Z) > 225 or (ClientFurniture.pos.Position - Hull.Intertele.Position).Magnitude < 10 or player.Character.Head.Position.Y - Hull.Hull.Position.Y > Hull.ShipClass.MaxY.Value then
						GoodToplace = false
						if player.Character.Head.Position.Y - Hull.Hull.Position.Y > Hull.ShipClass.MaxY.Value then
							GUI.Enabled = false
							game.ReplicatedStorage.Remotes.PlayerEvents.InteriorChange:FireServer("Outside")
							ClientFurniture:Destroy()
						end
					else
						GoodToplace = true
					end
					
					if GoodToplace == false then
						ClientFurniture.Highlight.BrickColor = BrickColor.new("Crimson")
					else
						ClientFurniture.Highlight.BrickColor = BrickColor.new("Cyan")
					end
				end
			end)

and here is the part where if someone clicks:

			UIS.InputBegan:Connect(function(key, ended)
				if key.UserInputType == Enum.UserInputType.MouseButton1  then
					if hitter ~= nil and placing == true and GoodToplace == true then
						game.ReplicatedStorage.Remotes.Functions.PlaceFurniture:FireServer(ClientFurniture.Name, SendPosition, workspace.rot.Orientation)
						placing = false
						for x, D in pairs(GUI.ScrollingFrame:GetChildren()) do
							if D:IsA("TextButton") then
								if player.DataStoreFolder.PlayerFurniture:FindFirstChild(D.Name).Value > 0 then
									D.Visible = true
								end
							end
						end
						GUI.RemoveBt.Visible = true
						FurnitureOwned.Value = FurnitureOwned.Value - 1
						local PlacedFurn = player.DataStoreFolder.PlayerFurniture.Dictionary.Furniture:Clone()
						PlacedFurn.Name = ClientFurniture.Name
						GUI.Furn:Play()
						rot:Destroy()
						ClientFurniture:Destroy()
						placing = false
						Stepped:Disconnect()
					end
				end
-- after this is some lines rotating furniture

Doesn’t Weld.C0.Y hold the relative Y offset? Or C1 depending how you set it up. Using the weld created whenever you first place an item.

There seems to be a lot of over complication regarding orientation and stuff in your code.

As a side note, the first of these two doesn’t provide anything useful if you immediately overwrite it:

1 Like

Im not sure, but yeah looking back I might have over-complicated it. However since whoever the network owner of the ship will never see the furniture (since it would be in replicated sotrage) the weld actually never “applies” but I added it incase if somehow the networkowner of the ship sees the furniture. I tested this out, and the weld is fine even if the network owner “sees” the furniture

If you noticed in the video I attached, the first time you place furniture, it its normal. Its weird that when the ship gets re-spawned, the furniture placement is wonky

edit: that part you quoted is over-complicated on my part, I used a part called “rot” which rotates, and it‘s Y-Orientation is saved. Looking back it was unnecessary, and I will replace it just recording the “pos” part‘s rotation

I am trying Weld.C0.Y and its going well, since now all the furniture have the same 3-stud Y-Axis offset, instead of having random Y-Axis offsets. I‘m gonna try testing this many times to confirm if it is always a 3 stud offset.

At least if it’s consistent then it likely is due to some part being in a different place to the client or some difference between what the server and client sees.

Out of curiosity - when you place the first time and it places correctly, do other players also see the item in the correct location?

Either way, if counteracting your 3 stud offset fixes the issue for all ships and all orientations during placement, then I think that’s a fair way to resolve it.

1 Like

so, basically 1/3 of a ship’s interior is bellow the waterline. Players enter their sailboats and sailships by clicking a hatch or door. When they click it, they enter the ship/boat and the client takes the ship‘s furniture folder from Replicatedstorage, and parents it to the ship. Then the waterline is lowered to the very bottom of the ship (locally) so water and waves don’t clip in.

When the furniture is placed, according to what the server sees, the furniture gets placed in the furniture folder, in replicatedstorage. And since the server (or whoever is the network owner) thinks that, the furniture to the client(s) inside the ship see it moving with the ship (regardless if its welded or not) as if it is welded.

1 Like

I just finished testing the furniture on every ship. And yup, they all have a 3 stud offset! Thanks for the solution, I was going crazy