Getting parts to be positioned in relation to a part and not the global world

Problem here: Part is being placed 2 studs away from the grid,


but if I move the baseplate back 2 studs in studio, and go again,

It’s in line. Now I know I could just do that and there is then no problem. However, if I have 15-16 of these plots scattered around a map, I don’t want to have to go each one and make sure they are all on the same ‘world’ grid. I want the game to base the walls positioning (and thus future items positions) on the base parts position, and not just a world position. I’ve always used base.Position and base.Size, base.CFrame for everything so I don’t know why when I move the bases position it alters the position of these walls.

RoundModule

local round = {}

function roundNumber(number, to)
	to = to or 1
	return math.floor(number/to + 0.5) * to
end

function round:Vector(vector, unit, yValue)
	return Vector3.new(roundNumber(vector.X, unit), yValue, roundNumber(vector.Z, unit))
end

return round

Positioning

local renderPosition = {}

local round = require(script.Parent.Round)
local clampMouse = require(script.Parent.ClampMouse)

local gridSize = 5

function renderPosition:Render(playersPlot, mouseP, pole, lowerX, upperX, lowerZ, upperZ)
	local mouseClampedP = clampMouse:Clamp(mouseP, lowerX, upperX, lowerZ, upperZ)

	pole.CFrame = CFrame.new(
		round:Vector(mouseClampedP + Vector3.new(0, pole.Size.X/2, 0), 
		gridSize, 
		playersPlot.Base.Position.Y + (pole.Size.X/2) + 0.05)
	)*CFrame.Angles(0, 0, math.rad(90))
end

return renderPosition

MouseClamp

local clampMouse = {}

function clampMouse:Clamp(mouseP, lowerX, upperX, lowerZ, upperZ)
	local mouseClampedP =	Vector3.new(
								math.clamp(mouseP.X, lowerX, upperX),
						  		mouseP.Y,
						  		math.clamp(mouseP.Z, lowerZ, upperZ)
	 						)
	
	return mouseClampedP
end

return clampMouse

If I am missing anything let me know! :+1:

4 Likes

To convert between object space and world space coordinates, you’ll want the :ToWorldSpace and :ToObjectSpace methods of CFrame: check them out here.

Here’s a quick explanation:
Say you want a part to always be positioned 2 studs in front of and 3 to the left of the camera. The part’s coordinates in the camera’s object space should always be (-3, 0, -2). But the camera can be anywhere, so the part’s world space coordinates could also be anywhere. So we’ll want to update the CFrame of the part every frame to (-3, 0, -2) in the camera’s object space.

We know the object space coordinates, and want to calculate the world space coordinates, because when we set part.CFrame to something it’s always in world coordinates. We do this like so: part.CFrame = game.Workspace.CurrentCamera.CFrame:ToWorldSpace(CFrame.new(-3, 0, -2)). This sets the part’s CFrame to the offset, but converted from the camera’s object space to world space coordinates.

You can think of object space coordinates as a position (and rotation) in relation to some other position and rotation. Keeping track of which CFrame is being converted to which CFrame’s object space is pretty difficult. Just remember that it’s objectSpaceToConvertFrom:ToWorldSpace(coordinatesToConvertToWorldSpace) Hope this makes sense :stuck_out_tongue:

In your specific case, you’ll want to convert the mouse position to the grid’s object space coordinates before rounding (:ToObjectSpace). You don’t care that the coordinates are rounded in world space, you only care that they’re rounded in relation to the grid. Of course, when you actually set the CFrame of the part, you need to convert back to world space coordinates (after rounding).

Let us know if it works :slight_smile:

EDIT: Oh, and here’s an example that might clarify the whole world and object space thing:

game:GetService("RunService").RenderStepped:Connect(function()
    game.Workspace.Part.CFrame = game.Workspace.CurrentCamera.CFrame:ToWorldSpace(CFrame.new(-2, 0, -3))
end)

Just put that code in a LocalScript in StarterGui, and make sure there’s a part called ‘Part’ in workspace.

9 Likes

It’s a description but not really what I am trying to go for. There should be no numbers, like -3, 2 etc. I’m not moving parts based on a stud reference. I want parts to be positioned in relation to a baseplate object, which has a predetermined size.

But currently the parts are being placed on a 5x5 grid of the world, instead of a 5x5 grid of the baseplate, so when I move this baseplate and the 5x5 grid on this baseplate moves, the part doesn’t move with it

There should be no numbers, like -3, 2 etc. I’m not moving parts based on a stud reference.

Sorry, I should have made it more clear. That was just part of an example to help you understand different coordinate spaces.

Here’s a code example of how you might solve your specific problem:

local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local baseplate = game.Workspace:FindFirstChild("Baseplate")

local gridSize = 4 --the side length of the grid cells

function round(n, to)
	--Rounds a number n to the nearets multiple of to
	return math.floor(n/to + 0.5) * tp
end

function roundVector3(v3, to)
	--Rounds the components of a Vector3 v3 to the nearest multiple of to
	return Vector3.new(
		round(v3.X, to),
		round(v3.Y, to),
		round(v3.Z, to)
	)
end

function toRoundedWorldspace(part, worldPosition, gridSize)
	--Takes a part and a position in world space, as well as a grid size to snaap the position to

	--Convert to the part's coordinate space
	local partPosition = part.CFrame:ToObjectSpace(worldPosition)

	--Round to nearest gridSize, in the part's coordinate space
	local roundedPartPosition = roundVector3(partPosition, gridSize)

	--Convert back to world space
	local roundedWorldPosition = part.CFrame:ToWorldSpace(roundedPartPosition)
	
	return roundedWorldPosition
end

function mousePosOnPart(part, gridSize)
	--Returns the mouse's position on a part, rounded to nearest gridSize
	local worldPos = mouse.Position
	return toRoundedWorldspace(baseplate, worldPos, gridSize)
end
4 Likes