Rotating previewPart using :GetPivot() Method

Hello! I’m currently trying to make placement system for my Roblox game, however, I’ve ran through an issue, I wan’t the block to be rotated using the Pivot system, like, the previewPart would rotate based on the Pivot’s position, so anyone can help?

Here’s my client-script:

-- Fun fact: You can declare multiple variables in one line like this:
-- WaitForChild is used here because it takes time to replicate objects to client and this function waits for object with certain name to be on the client
local players, contextActionService, replicatedStorage = game:GetService("Players"), game:GetService("ContextActionService"), game:GetService("ReplicatedStorage")
local localPlayer = players.LocalPlayer
local ignoreForMouse, buildingModel = workspace.__PLOTS__:WaitForChild(localPlayer.Name.."'s PLOT"):WaitForChild("IgnoreForMouse"), workspace.__PLOTS__:WaitForChild(localPlayer.Name.."'s PLOT"):WaitForChild("BuildingModel")
local previewPart = localPlayer:WaitForChild("PREVIEW_PART")
local mouse = localPlayer:GetMouse()
local tweenservice = game:GetService("TweenService")
local tool = script.Parent
local remoteFunction = tool:WaitForChild("RemoteFunction")
local canUse, isEquipped, isBuillding, targetToDestroy, orginalColor = true, false, true, nil, nil
local selectionBox, baseplate = previewPart.Value:WaitForChild("SelectionBox"), workspace.__PLOTS__:WaitForChild(localPlayer.Name.."'s PLOT"):WaitForChild("__BASEPLATE__")
local BuildingUi = tool.__BUILDING_UI__
local O_M_O_E = localPlayer.O_M_O_E
local rotation = tool.__ROTATION__
local character = localPlayer.Character
if not character or not character.Parent then character = localPlayer.CharacterAdded:Wait() end
local humanoid = character:WaitForChild("Humanoid")

local function calculateOffset(positionNumber) -- It is used for math to calculate block position
	if positionNumber * 10 % 3 == 1 then
		return -1
	elseif  positionNumber * 10 % 3 == 2 then
		return 1
	else
		return 0
	end
end

local function UI_ANIMATION_1()
	local __1__ = tweenservice:Create(BuildingUi.__BACKGROUND__,TweenInfo.new(1,Enum.EasingStyle.Elastic),{Position = UDim2.new(0.5,0,0.5,0)});
	__1__:Play();
	local __2__ = tweenservice:Create(workspace.CurrentCamera,TweenInfo.new(1,Enum.EasingStyle.Elastic),{FieldOfView = 50});
	__2__:Play();
	local __3__ = tweenservice:Create(game.Lighting._UI_BLUR,TweenInfo.new(1,Enum.EasingStyle.Elastic),{Size = 14});
	__3__:Play();
end

local function UI_ANIMATION_2()
	local __1__ = tweenservice:Create(BuildingUi.__BACKGROUND__,TweenInfo.new(1,Enum.EasingStyle.Elastic),{Position = UDim2.new(0.5,0,1.4,0)});
	__1__:Play();
	local __2__ = tweenservice:Create(workspace.CurrentCamera,TweenInfo.new(1,Enum.EasingStyle.Elastic),{FieldOfView = 70});
	__2__:Play();
	local __3__ = tweenservice:Create(game.Lighting._UI_BLUR,TweenInfo.new(1,Enum.EasingStyle.Elastic),{Size = 0});
	__3__:Play();
	local __4__ = tweenservice:Create(BuildingUi.__BOTTOM__,TweenInfo.new(1,Enum.EasingStyle.Elastic),{Position = UDim2.new(0.5,0,1.4,0)});
	__4__:Play();
end

local function UI_ANIMATION_3()
	local __1__ = tweenservice:Create(BuildingUi.__BOTTOM__,TweenInfo.new(1,Enum.EasingStyle.Elastic),{Position = UDim2.new(0.5,0,0.762,0)});
	__1__:Play();
end

local function toggleMode(actionName, inputState, inputObject) -- Toggled modes (building / destroying)
	if inputState == Enum.UserInputState.Begin and canUse then -- It activates every time when a buttons state is changing so it is important to check the state of the button. Here, the two conditions can be true only if a player started holding RMB (it doesn't matter for how long)
		isBuillding = not isBuillding
	end
end

selectionBox.Color3 = Color3.fromRGB(0, 255, 0)
mouse.TargetFilter = ignoreForMouse -- Player's mouse should ignore the preview part and pretend that it doesn't exist
game:GetService("RunService").RenderStepped:Connect(function() -- This event fires "for every frame generated by a client". The definition from Roblox API reference is: Fires every frame prior to the frame being rendered
	local mouseTarget, mouseHit, mouseSurface = mouse.Target, mouse.Hit, mouse.TargetSurface
	if mouseTarget and isEquipped and humanoid.Health ~= 0 then -- Player's mouse should point at something, player's character should have the tool equipped and the character should be not dead		
		if isBuillding then -- If using building mode, there nothing to request to destroy
			targetToDestroy = nil -- /\
			if mouseTarget == baseplate then -- Trying to place a block on the Baseplate
				-- That's the whole math for grid system for this building system. Get a piece of paper and something to write and do the math by yourself if you struggle to understand it
				local x,z = math.floor(mouseHit.X)+0.5, math.floor(mouseHit.Z)+0.5
				local blockSizeX = previewPart.Value.Size.X
				local blockSizeZ = previewPart.Value.Size.Z
				x = x - ((blockSizeX % 2 == 0) and 0.5 or 0) -- If the blockSize is even then subtract 0.5, if not then subtract 0
				z = z - ((blockSizeZ % 2 == 0) and 0.5 or 0) -- Do the same for the Z axis.
				previewPart.Value.CFrame = CFrame.new(
					x+calculateOffset(x),
					(mouseTarget.Size.Y + previewPart.Value.Size.Y) / 2 + mouseTarget.Position.Y,
					z+calculateOffset(z)
				) * CFrame.Angles(0,math.rad(tool.__ROTATION__.Value),0)
				previewPart.Value.Parent = ignoreForMouse -- Ready to place a block
			elseif mouseTarget.Parent == buildingModel then
				if mouseSurface == Enum.NormalId.Back then -- The preview block should move away from an actual block in a different direction depending on a pointed surface
					previewPart.Value.CFrame = mouseTarget.CFrame:ToWorldSpace(CFrame.new(0, 0, mouseTarget.Size.Z)) -- This is less complex math, just moving the preview block far enough from an actual block
				elseif mouseSurface == Enum.NormalId.Bottom then
					previewPart.Value.CFrame = mouseTarget.CFrame:ToWorldSpace(CFrame.new(0, -mouseTarget.Size.Y, 0))
				elseif mouseSurface == Enum.NormalId.Front then
					previewPart.Value.CFrame = mouseTarget.CFrame:ToWorldSpace(CFrame.new(0, 0, -mouseTarget.Size.Z))
				elseif mouseSurface == Enum.NormalId.Left then
					previewPart.Value.CFrame = mouseTarget.CFrame:ToWorldSpace(CFrame.new(-mouseTarget.Size.X, 0, 0))
				elseif mouseSurface == Enum.NormalId.Right then
					previewPart.Value.CFrame = mouseTarget.CFrame:ToWorldSpace(CFrame.new(mouseTarget.Size.X, 0, 0))
				elseif mouseSurface == Enum.NormalId.Top then
					local x,z = math.floor(mouseHit.X)+0.5, math.floor(mouseHit.Z)+0.5
					local blockSizeX = previewPart.Value.Size.X
					local blockSizeZ = previewPart.Value.Size.Z
					x = x - ((blockSizeX % 2 == 0) and 0.5 or 0) -- If the blockSize is even then subtract 0.5, if not then subtract 0
					z = z - ((blockSizeZ % 2 == 0) and 0.5 or 0) -- Do the same for the Z axis.
					previewPart.Value.CFrame = CFrame.new(
						x+calculateOffset(x),
						(mouseTarget.Size.Y + previewPart.Value.Size.Y) / 2 + mouseTarget.Position.Y,
						z+calculateOffset(z)
					) * CFrame.Angles(0,math.rad(tool.__ROTATION__.Value),0)
				end
				previewPart.Value.Parent = ignoreForMouse -- Ready to place a block
			else
				previewPart.Value.Parent = replicatedStorage.__PREVIEW_PARTS__ -- Move the preview part back to replicated storage because not enough conditions were met to place place or destroy block preview (an example: trying to place a block 500 blocks away while max distance is smaller than 500)
			end

		elseif mouseTarget and mouseTarget.Parent == buildingModel then -- Player wants to destory a block
			targetToDestroy = mouseTarget
			previewPart.Value.CFrame = mouseTarget.CFrame * CFrame.Angles(0,math.rad(tool.__ROTATION__.Value),0) -- Move the preview block to a potential block to destroy position
			previewPart.Value.Parent = ignoreForMouse

		else
			previewPart.Value.Parent = replicatedStorage.__PREVIEW_PARTS__
		end

	end
end)

tool.Equipped:Connect(function() -- Set isEquipped boolean to true if the tool is equipped
	isEquipped = true
	BuildingUi.Parent = localPlayer.PlayerGui
	BuildingUi.Enabled = true
	if O_M_O_E.Value == true then
		UI_ANIMATION_1()
	elseif O_M_O_E.Value == false then
		UI_ANIMATION_3()
	end
end)

tool.Unequipped:Connect(function() -- Set isEquipped boolean to false if the tool is unequpped
	isEquipped = false
	previewPart.Value.Parent = replicatedStorage.__PREVIEW_PARTS__
	BuildingUi.Parent = tool
	UI_ANIMATION_2()
	BuildingUi.Enabled = false
end)

humanoid.Died:Connect(function() -- Doing some clearing so the building system will work after respawn
	previewPart.Value.Parent = replicatedStorage.__PREVIEW_PARTS__
	selectionBox.Color3 = Color3.fromRGB(0, 255, 0)
end)

tool.Activated:Connect(function()
	if canUse and previewPart.Value.Parent == ignoreForMouse and humanoid.Health ~= 0 then -- Check if no block is being destroyed or placed by this tool, if preview part in visible and if the player's character is alive (health not equal to 0)
		canUse = false -- It will prevent from spamming blocks
		orginalColor = selectionBox.Color3
		selectionBox.Color3 = Color3.fromRGB(0, 255, 0)
		remoteFunction:InvokeServer(previewPart.Value.CFrame,targetToDestroy) -- This event will "freeze" here until placing or destroying a block is completed
		workspace.__SFX__._PLACE_:Play()
		previewPart.Value.Parent = replicatedStorage.__PREVIEW_PARTS__ -- Preview at a destroyed block position is not needed
		canUse = true -- After waiting to place or destroy a block, player can use this tool again
		selectionBox.Color3 = Color3.fromRGB(0, 255, 0)
	end
end)

You want to :GetPivot() of what exactly?

1 Like

Hello! I want the :GetPivot() to be about block’s rotation.

I imagined something like this:

local part = -- here's your part

local rotation_speed = 0.2
local rotation = 0

while task.wait() do
	
	rotation += rotation_speed
	
	part.CFrame = part:GetPivot() * CFrame.Angles(0,math.rad(rotation),0)
	
end
1 Like