Round part based on angle of previous segment

I’m wanting to round the players second pole to a 45 degree angle. Atm, I have to just deny the pole movement unless you on that angle. Problem is, if I move my mouse out to extend the length, it doesn’t move unless I’m on that 45 degree

local function Angle(vectorA, vectorB)
	if vectorA.X == vectorB.X then
		return true
	elseif vectorA.Z == vectorB.Z then
		return true
	else
		return math.abs((vectorB - vectorA).X) == math.abs((vectorB - vectorA).Z)
	end
end

local Pole1 = pole.Parent.Pole1
		
local NewCFrame = CFrame.new(
	Round(
		MouseClampedP + Vector3.new(0, pole.Size.X / 2, 0),
		5,
		playersPlot.Base.Position.Y + (pole.Size.X / 2) + 0.05)
) * CFrame.Angles(0, 0, math.rad(90))

local Angled = Angle(Pole1.Position, NewCFrame.Position)
if not Angled then return end

pole:PivotTo(NewCFrame)
-- Round
function RoundNumber(number, to)
	to = to or 1
	
	return math.floor(number/to + 0.5) * to
end

return function(vector, unit, yValue)
	return Vector3.new(RoundNumber(vector.X, unit), math.floor(yValue), RoundNumber(vector.Z, unit))
end

Basically, instead of

local Angled = Angle(Pole1.Position, NewCFrame.Position)
if not Angled then return end

I want the angle to be locked in with the NewCFrame, so I don’t have to do return if it’s not the angle

I didn’t quite get you, but by reading the post I assume you want to snap rotation of the wall too along with movement. Here’s a quick snap code I wrote that achieves that using some basic trigonometry:

local function snap(vector1, vector2)
	if vector2 then
		local angle = math.round(math.atan2(vector2.Z - vector1.Z, vector2.X - vector1.X) / ROTATION_CONSTRAINT) * ROTATION_CONSTRAINT;
		local dist = math.abs((vector1 - vector2).Magnitude);
		vector1 += Vector3.new(dist * math.cos(angle), 0, dist * math.sin(angle));
	end
	
	local snapped = {}
	for i,axis in ipairs{ "X", "Y", "Z" } do
		snapped[i] = math.round(vector1[axis] / MOVE_CONSTRAINT) * MOVE_CONSTRAINT;
	end
	return Vector3.new(table.unpack(snapped));
end

And here’s the code I used to test:

local userInputService = game:GetService("UserInputService");
local workspace = game:GetService("Workspace");

local camera = workspace.CurrentCamera;

local MOVE_CONSTRAINT = 4;
local ROTATION_CONSTRAINT = math.rad(45);

local cPole1, cWall, cPole2;

local function snap(vector1, vector2)
	if vector2 then
		local angle = math.round(math.atan2(vector2.Z - vector1.Z, vector2.X - vector1.X) / ROTATION_CONSTRAINT) * ROTATION_CONSTRAINT;
		local dist = math.abs((vector1 - vector2).Magnitude);
		vector1 += Vector3.new(dist * math.cos(angle), 0, dist * math.sin(angle));
	end
	
	local snapped = {}
	for i,axis in ipairs{ "X", "Y", "Z" } do
		snapped[i] = math.round(vector1[axis] / MOVE_CONSTRAINT) * MOVE_CONSTRAINT;
	end
	return Vector3.new(table.unpack(snapped));
end

local function updateWall(wall, p1, p2)
	wall.Size = Vector3.new(1, 15, math.abs((p1 - p2).Magnitude));
	wall.CFrame = CFrame.new((p1 + p2) / 2, p2);
end

local function createWall()
	local wall = Instance.new("Part");
	wall.Name = "Wall";
	wall.Anchored = true;
	wall.Parent = workspace;
	return wall;
end

local function updatePole(pole, p)
	p = cPole1 and snap(cPole1.Position, p) or snap(p);
	pole.Position = p;
end

local function createPole(pos)
	local newPole = cPole1 and cPole1:Clone();
	if not newPole then
		newPole = Instance.new("Part");
		newPole.Size = Vector3.new(1, 15, 1);
		newPole.Transparency = 0.5;
		newPole.CanCollide = false;
		newPole.Anchored = true;
	end
	updatePole(newPole, pos);
	
	if cPole1 then
		cWall = createWall(cPole1.Position, newPole.Position);
		cPole2 = newPole;
	else
		cPole1 = newPole;
	end

	newPole.Position = pos;
	newPole.Parent = workspace;
	
	return newPole;
end

local function preview(pos)
	if cWall then
		updatePole(cPole2, pos);
		updateWall(cWall, cPole1.Position, cPole2.Position);
	else
		if cPole1 then updatePole(cPole1, pos); end
	end
end

local function getWorldPosition(sPos)
	local unit = camera:ScreenPointToRay(sPos.X, sPos.Y);
	local res = workspace:Raycast(unit.Origin, unit.Direction * 30, RaycastParams.new());
	if res then return res.Position; end
end

userInputService.InputBegan:Connect(function(key, processed)
	if processed then return; end
	if key.UserInputType ~= Enum.UserInputType.MouseButton1 then return; end
	
	if cPole1 and cPole2 and cWall then
		cPole1 = nil;
		cPole2 = nil;
		cWall = nil;
		return;
	end
	
	local pos = getWorldPosition(key.Position);
	if pos then
		createPole(pos);
	end
end);

userInputService.InputChanged:Connect(function(key, processed)
	if processed then return; end
	if key.UserInputType ~= Enum.UserInputType.MouseMovement then return; end
	
	local pos = getWorldPosition(key.Position);
	if pos then	
		preview(pos);	
	end
end);

EDIT:

  • Fixed a bug where the 2nd pole position didn’t snap after rotating
  • Implemented preview for fun (which made me detect the first bug)
1 Like

I need it still update the walls size as I move the mouse. But if my mouse is not in a 45 degree angle, it won’t move at all, due to

local Angled = Angle(Pole1.Position, NewCFrame.Position)
if not Angled then return end

Correct me if I’m wrong, isn’t this what you’re asking for?

Yee

more charactersmore charactersmore charactersmore charactersmore characters

I didn’t alter any of my code to attain that, the code posted in my previous post achieves that already.

It needs to work with poles tho. Cause of how Roblox orients them, they get rotated weirdly :confused:

I made the Pole2 face the Pole1 just by changing the updatePole() function:

local function updatePole(pole, p)
	p = cPole1 and snap(cPole1.Position, p) or snap(p);
	pole.CFrame = cPole2 and CFrame.new(p, cPole1.Position) or CFrame.new(p);
end

For your cylinder being rotated like that, try multiplying a CFrame.fromEulerAnglesXYZ(math.rad(90), 0, 0):

pole.CFrame = (cPole2 and CFrame.new(p, cPole1.Position) or CFrame.new(p)) * CFrame.fromEulerAnglesXYZ(math.rad(90), 0, 0);

Unsure what I am doing wrong :confused:

local Pole1 = pole.Parent.Pole1
		local NewCFrame = CFrame.new(
			Round(
				MouseClampedP + Vector3.new(0, pole.Size.X / 2, 0),
				5,
				playersPlot.Base.Position.Y + (pole.Size.X / 2) + 0.05)
		) * CFrame.Angles(0, 0, math.rad(90))
		
		pole.Position = snap(Pole1.Position, NewCFrame.Position)
		pole:PivotTo(pole.CFrame * CFrame.fromEulerAnglesXYZ(math.rad(90), 0, 0))

also tried

local Pole1 = pole.Parent.Pole1
		local NewCFrame = CFrame.new(
			Round(
				MouseClampedP + Vector3.new(0, pole.Size.X / 2, 0),
				5,
				playersPlot.Base.Position.Y + (pole.Size.X / 2) + 0.05)
		) * CFrame.Angles(0, 0, math.rad(90))
		local pos = snap(Pole1.Position, NewCFrame.Position)
		pole.CFrame = CFrame.new(pos, Pole1.Position) * CFrame.fromEulerAnglesXYZ(math.rad(90), 0, 0);

same problem

Managed to get it working, but problem is it doesn’t clamp to the grind, meaning they can have the pole outside of the grind

local MouseClampedP = ClampMouse(MouseHit.Position, lowerX, upperX, lowerZ, upperZ)

local Pole1 = pole.Parent.Pole1
		local NewCFrame = CFrame.new(
			Round(
				MouseClampedP + Vector3.new(0, pole.Size.X / 2, 0),
				Constants.GRID_SIZE,
				playersPlot.Base.Position.Y + (pole.Size.X / 2) + 0.05)
		) * CFrame.Angles(0, 0, math.rad(90))
		
		local SnappedPos = Snap(Pole1.Position, NewCFrame.Position)
		pole:PivotTo(CFrame.new(SnappedPos + Vector3.new(0, 1, 0)) * CFrame.fromEulerAnglesXYZ(0, 0, math.rad(90)))

Point of ClampMouse basically clamps the mouse position to be within the bounding area of the grid, but obvs this isn’t working with rotated walls