I made a placement system but I am having trouble figuring out how to make the rotation work on mobile.
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local CollectionService = game:GetService("CollectionService")
local ClientModules = ReplicatedStorage:WaitForChild("ClientModules")
local inventory = require(ClientModules:WaitForChild("InventoryClient"))
local player = Players.LocalPlayer
local mouse = player:GetMouse()
local camera = Workspace.CurrentCamera
local matchRotation = false
local gridSize = 1
local rotationIncrement = math.rad(15)
local tiltIncrement = math.rad(15)
local currentRotation = 0
local currentTilt = 0
local module = {}
local mobileConfirmation = script.BuildConfirmationMobile
local autoplace = false
local autoplaceOnColor = Color3.fromRGB(140, 0, 255)
local autoplaceOffColor = Color3.fromRGB(46, 0, 86)
local ghostModel
local renderConn
local inputConn
local rotConn
local tiltConn
local inputBeganConn
local enabled = false
local lastPlacementCFrame = nil
local newModels = {}
local function addBlockSound(pivot, sound)
local soundPartPos = Instance.new("Part")
soundPartPos.Anchored = true
soundPartPos.Transparency = 1
soundPartPos.CanCollide = false
soundPartPos.Size = Vector3.new(0.01,0.01,0.01)
local soundPartParent = Instance.new("Model")
soundPartParent.Name = "SoundPart"
soundPartPos.Parent = soundPartParent
soundPartParent:PivotTo(pivot)
soundPartParent.Parent = workspace
sound.Parent = soundPartPos
sound:Play()
game.Debris:AddItem(soundPartParent, sound.TimeLength+0.1)
end
local function placeServer(model, pivot, blockId)
local blockFunc = ReplicatedStorage.Remotes.PlaceBlockServer:InvokeServer(pivot, blockId)
if blockFunc.Result == "Success" or blockFunc.Result == "Rejected" then
model:Destroy()
if blockFunc.Result == "Success" then
inventory:PlaceBlockUpdate(blockId, blockFunc.Result)
local sound = script.PlaceSound:Clone()
addBlockSound(pivot, sound)
end
if blockFunc.Result == "Rejected" then
inventory:PlaceBlockUpdate(blockId, blockFunc.Result)
local sound = script.RejectPlaceSound:Clone()
addBlockSound(pivot, sound)
end
end
end
local function roundToGrid(value)
return math.round(value / gridSize) * gridSize
end
local function anchorModel(model, anchored)
for _, part in pairs(model:GetDescendants()) do
if part:IsA("BasePart") then
part.Anchored = anchored
end
end
end
local function setModelTransparency(model, transparency)
for _, part in pairs(model:GetDescendants()) do
if part:IsA("BasePart") then
part.Transparency = transparency
part.CanCollide = false
end
end
end
local function getModelSize(model)
return model:GetExtentsSize()
end
local function getPlacementCFrame()
if not ghostModel then return nil end
local origin = camera.CFrame.Position
local direction = (mouse.Hit.Position - origin).Unit * 1000
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {player.Character, ghostModel}
rayParams.FilterType = Enum.RaycastFilterType.Exclude
local result = workspace:Raycast(origin, direction, rayParams)
if not result then return nil end
local hitPos = result.Position
local normal = result.Normal
local target = result.Instance
if CollectionService:HasTag(target.Parent, "IgnorePlacement") then return end
local function getOffsetInNormalDirection(modelCFrame, modelSize, normal)
local halfSize = modelSize / 2
local axisX = modelCFrame.RightVector * halfSize.X
local axisY = modelCFrame.UpVector * halfSize.Y
local axisZ = modelCFrame.LookVector * halfSize.Z
return math.abs(normal:Dot(axisX)) + math.abs(normal:Dot(axisY)) + math.abs(normal:Dot(axisZ))
end
local size = getModelSize(ghostModel)
local up = normal.Unit
local targetLook = target.CFrame.LookVector
local targetRight = target.CFrame.RightVector
local forward = (targetLook - up * targetLook:Dot(up))
if forward.Magnitude < 0.001 then
forward = (targetRight - up * targetRight:Dot(up))
end
forward = forward.Unit
local right = forward:Cross(up).Unit
local baseCFrame = CFrame.fromMatrix(Vector3.zero, right, up)
local rotationCFrame = CFrame.Angles(currentTilt, currentRotation, 0)
local modelOrientationCFrame
if matchRotation then
modelOrientationCFrame = CFrame.fromMatrix(Vector3.zero, right, up) * CFrame.Angles(currentTilt, currentRotation, 0)
else
modelOrientationCFrame = CFrame.Angles(currentTilt, currentRotation, 0)
end
local offset = getOffsetInNormalDirection(modelOrientationCFrame, size, normal)
local exactPos = hitPos + normal * offset
local targetCFrame = target.CFrame
local localPos = targetCFrame:PointToObjectSpace(exactPos)
local localNormal = targetCFrame:VectorToObjectSpace(normal)
local snapX = math.abs(localNormal.X) < 0.9 and roundToGrid(localPos.X) or localPos.X
local snapY = math.abs(localNormal.Y) < 0.9 and roundToGrid(localPos.Y) or localPos.Y
local snapZ = math.abs(localNormal.Z) < 0.9 and roundToGrid(localPos.Z) or localPos.Z
local snappedLocalPos = Vector3.new(snapX, snapY, snapZ)
local snappedWorldPos = targetCFrame:PointToWorldSpace(snappedLocalPos)
local finalBaseCFrame = CFrame.fromMatrix(snappedWorldPos, right, up)
return finalBaseCFrame * rotationCFrame
end
local function onInputBegan(input, gameProcessed)
if gameProcessed then return end
if not enabled then return end
if input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.T then
currentRotation = currentRotation + rotationIncrement
elseif input.KeyCode == Enum.KeyCode.R then
currentTilt = currentTilt + tiltIncrement
end
end
end
local yesConn
local noConn
local autoConn
local function placeBlock(model, pivot, blockId)
model:PivotTo(pivot)
if UserInputService.TouchEnabled == true then
mobileConfirmation.Parent = player.PlayerGui
if yesConn then yesConn:Disconnect() end
if noConn then noConn:Disconnect() end
if autoConn then autoConn:Disconnect() end
yesConn = mobileConfirmation.Yes.MouseButton1Click:Connect(function()
mobileConfirmation.Parent = script
placeServer(model, pivot, blockId)
if yesConn then yesConn:Disconnect() end
if noConn then noConn:Disconnect() end
end)
noConn = mobileConfirmation.No.MouseButton1Click:Connect(function()
mobileConfirmation.Parent = script
model:Destroy()
if yesConn then yesConn:Disconnect() end
if noConn then noConn:Disconnect() end
end)
autoConn = mobileConfirmation.AutoPlace.MouseButton1Click:Connect(function()
autoplace = not autoplace
if autoplace then
mobileConfirmation.AutoPlace.Stroke.Color = autoplaceOnColor
mobileConfirmation.AutoPlace.Image.ImageColor3 = autoplaceOnColor
else
mobileConfirmation.AutoPlace.Stroke.Color = autoplaceOffColor
mobileConfirmation.AutoPlace.Image.ImageColor3 = autoplaceOffColor
end
end)
else
placeServer(model, pivot, blockId)
end
end
function module.Enable(tool)
if enabled or not inventory.selectedBlock then return end
enabled = true
inventory:Toggle(true)
ghostModel = inventory.selectedBlock:Clone()
anchorModel(ghostModel, true)
if UserInputService.TouchEnabled == false then
setModelTransparency(ghostModel, 0.5)
else
setModelTransparency(ghostModel, 1)
end
ghostModel.Name = "GhostModel"
ghostModel.Parent = workspace
mouse.TargetFilter = ghostModel
currentRotation = 0
currentTilt = 0
renderConn = RunService.RenderStepped:Connect(function()
if mobileConfirmation.Parent == script then
local cf = getPlacementCFrame()
if cf then
if matchRotation then
ghostModel:PivotTo(cf)
else
ghostModel:PivotTo(CFrame.new(cf.Position) * CFrame.Angles(currentTilt, currentRotation, 0))
end
end
local ghostBlockId = ghostModel:GetAttribute("BLOCKID")
if ghostBlockId and inventory.selectedBlock then
if ghostBlockId ~= inventory.selectedBlock:GetAttribute("BLOCKID") then
ghostModel:Destroy()
ghostModel = inventory.selectedBlock:Clone()
anchorModel(ghostModel, true)
if UserInputService.TouchEnabled == false then
setModelTransparency(ghostModel, 0.5)
else
setModelTransparency(ghostModel, 1)
end
ghostModel.Name = "GhostModel"
CollectionService:AddTag(ghostModel, "IgnorePlacement")
ghostModel.Parent = workspace
mouse.TargetFilter = ghostModel
end
end
end
end)
inputConn = mouse.Button1Down:Connect(function()
local cf = getPlacementCFrame()
if cf and inventory.selectedBlock then
if #newModels > 0 then
for _, v in pairs(newModels) do
local model = v
table.remove(newModels, table.find(newModels, v))
model:Destroy()
end
end
local newModel = inventory.selectedBlock:Clone()
table.insert(newModels, newModel)
anchorModel(newModel, true)
if UserInputService.TouchEnabled == false then
setModelTransparency(newModel, 0)
else
setModelTransparency(newModel, 0.5)
mouse.TargetFilter = newModel
end
CollectionService:AddTag(newModel, "IgnorePlacement")
newModel.Parent = workspace
if matchRotation then
newModel:PivotTo(cf)
placeBlock(newModel, cf, newModel:GetAttribute("BLOCKID"))
else
placeBlock(newModel, CFrame.new(cf.Position) * CFrame.Angles(currentTilt, currentRotation, 0), newModel:GetAttribute("BLOCKID"))
end
end
end)
rotConn = mobileConfirmation.Rotate.MouseButton1Click:Connect(function()
currentRotation = currentRotation + rotationIncrement
print(currentRotation)
end)
tiltConn = mobileConfirmation.Tilt.MouseButton1Click:Connect(function()
currentTilt = currentTilt + tiltIncrement
print(currentTilt)
end)
inputBeganConn = UserInputService.InputBegan:Connect(onInputBegan)
end
function module.Disable()
if not enabled then return end
enabled = false
inventory:Toggle(false)
if renderConn then
renderConn:Disconnect()
renderConn = nil
end
if inputConn then
inputConn:Disconnect()
inputConn = nil
end
if inputBeganConn then
inputBeganConn:Disconnect()
inputBeganConn = nil
end
if ghostModel then
ghostModel:Destroy()
ghostModel = nil
end
mobileConfirmation.Parent = script
for _, v in pairs(newModels) do
local model = v
table.remove(newModels, table.find(newModels, v))
model:Destroy()
end
end
function module.IsEnabled()
return enabled
end
return module
The rotation doesnt change when you click the rotation buttons but it would apply the rotation when you tap again.
I tried adding the button click events in different functions but it does the same thing.
The system is pretty much done for pc, I just need help with the rotation on mobile