You can write your topic however you want, but you need to answer these questions:
-
What do you want to achieve? Keep it simple and clear!
I’m making a placement system I have already made more compatible for mobile. -
What is the issue? Include screenshots / videos if possible!
I need to make it so that when on mobile, the preview object can be draggable, then they can press a speical place object button to place the object down. -
What solutions have you tried so far? Did you look for solutions on the Developer Hub?
I tried searching for other devforum posts, but none of them really match the same exact issue I’m trying to solve. I’ve tried making it so in the RenderStepped function, that the Mouse.Hit specifically needs to be over the preview object, in order to make it draggable, but that didn’t work either, and I’ve used ai to try and fix this issue, and I don’t really have any other ideas.
After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!
PlacementClient script:
-- Object placement, destruction, recolor, and general property functions
local PlacementEvent = game.ReplicatedStorage.PlacementEvent
local DestroyEvent = game.ReplicatedStorage.DestroyEvent
local RecolorEvent = game.ReplicatedStorage.RecolorEvent
local ObjectFolder = game.ReplicatedStorage:WaitForChild("ObjectFolder")
local Player = game.Players.LocalPlayer
local Mouse = Player:GetMouse()
local Frame = script.Parent:WaitForChild("ScrollingFrame")
local Destroy = script.Parent:WaitForChild("Destroy")
local Recolor = script.Parent:WaitForChild("Recolor")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local ownedProperty = nil
local PlacingObject = false
local DestroyingObject = false
local RecoloringObject = false
local RotatingObject = false
local red, green, blue = script.Parent.Red, script.Parent.Green, script.Parent.Blue
-- Check to see if the user is a mobile player, if not, then disable mobile placement guis.
if UserInputService.KeyboardEnabled and UserInputService.MouseEnabled then
script.Parent.PlaceObject.Visible = false
script.Parent.RotateObject.Visible = false
else
script.Parent.PlaceObject.Visible = true
script.Parent.RotateObject.Visible = true
end
-- Finds the player's owned property if available
local function findOwnedProperty()
ownedProperty = nil
for _, propertyType in ipairs(game.Workspace.Properties:GetChildren()) do
for _, property in ipairs(propertyType:GetChildren()) do
local propertyInfo = property:FindFirstChild("PropertyInfo")
if propertyInfo and propertyInfo.PropertyOwner.Value == Player then
ownedProperty = property
return
end
end
end
end
-- Initialize owned property at the start
findOwnedProperty()
-- Handles object destruction within owned property
Destroy.MouseButton1Click:Connect(function()
findOwnedProperty()
if ownedProperty then
if not PlacingObject and not DestroyingObject and not RecoloringObject then
DestroyingObject = true
Frame.Visible = false
Mouse.Button1Up:Connect(function()
if DestroyingObject then
local target = Mouse.Target
if target and target:IsDescendantOf(ownedProperty.PlacedItems) then
DestroyEvent:FireServer(target:FindFirstAncestorOfClass("Model"))
else
warn("Target not part of owned property.")
end
DestroyingObject = false
Frame.Visible = true
end
end)
end
else
warn("No owned property found.")
end
end)
-- Handles object recoloring within owned property
Recolor.MouseButton1Click:Connect(function()
findOwnedProperty()
if ownedProperty then
if not PlacingObject and not DestroyingObject and not RecoloringObject then
RecoloringObject = true
Frame.Visible = false
Mouse.Button1Up:Connect(function()
if RecoloringObject then
local target = Mouse.Target
if target and target:IsDescendantOf(ownedProperty.PlacedItems) then
RecolorEvent:FireServer(target:FindFirstAncestorOfClass("Model"), red.Text, green.Text, blue.Text)
else
warn("Target not part of owned property.")
end
RecoloringObject = false
Frame.Visible = true
end
end)
end
else
warn("No owned property found.")
end
end)
-- Additional utility to check if the input is on a GUI
local function isPointOnUI(inputPosition)
for _, uiElement in pairs(script.Parent:GetDescendants()) do
if uiElement:IsA("GuiObject") and uiElement.Visible then
local absolutePosition = uiElement.AbsolutePosition
local absoluteSize = uiElement.AbsoluteSize
local minX, maxX = absolutePosition.X, absolutePosition.X + absoluteSize.X
local minY, maxY = absolutePosition.Y, absolutePosition.Y + absoluteSize.Y
if inputPosition.X >= minX and inputPosition.X <= maxX and
inputPosition.Y >= minY and inputPosition.Y <= maxY then
return true
end
end
end
return false
end
-- Modify the button connection for placement
for _, Button in pairs(Frame:GetChildren()) do
if Button:IsA("TextButton") then
Button.MouseButton1Click:Connect(function()
if not PlacingObject then
PlacingObject = true
Frame.Visible = false
local RotationAmount = 0
local PreviewObject = ObjectFolder:FindFirstChild(Button.Name):Clone()
PreviewObject.Parent = game.Workspace
for _, Part in pairs(PreviewObject:GetDescendants()) do
if Part:IsA("BasePart") then
Part.Transparency = 0.5
Part.CanCollide = false
end
end
-- Rotation control for mobile
script.Parent.RotateObject.MouseButton1Down:Connect(function()
RotatingObject = true
while RotatingObject do
wait()
RotationAmount += 2
end
end)
script.Parent.RotateObject.MouseButton1Up:Connect(function()
RotatingObject = false
end)
-- Update object position and orientation during placement
RunService.RenderStepped:Connect(function()
if PlacingObject then
Mouse.TargetFilter = PreviewObject
if PreviewObject:FindFirstChild("MainPart") then
local ObjectCFrame = CFrame.new(Mouse.Hit.Position.X, Mouse.Hit.Position.Y + PreviewObject.PrimaryPart.Size.Y / 2, Mouse.Hit.Position.Z)
local ObjectAngles = CFrame.Angles(0, math.rad(RotationAmount), 0)
PreviewObject:SetPrimaryPartCFrame(ObjectCFrame * ObjectAngles)
end
end
end)
-- Finalize placement for mobile
script.Parent.PlaceObject.MouseButton1Click:Connect(function()
if PlacingObject and not isPointOnUI(UserInputService:GetLastInput().Position) then
PlacingObject = false
PlacementEvent:FireServer(PreviewObject.Name, PreviewObject.PrimaryPart.CFrame)
Frame.Visible = true
PreviewObject:Destroy()
end
end)
end
end)
end
end
-- Handles object placement
for _, Button in pairs(Frame:GetChildren()) do
if Button:IsA("TextButton") then
Button.MouseButton1Click:Connect(function()
if not PlacingObject then
PlacingObject = true
Frame.Visible = false
local RotationAmount = 0
local PreviewObject = ObjectFolder:FindFirstChild(Button.Name):Clone()
PreviewObject.Parent = game.Workspace
for _, Part in pairs(PreviewObject:GetDescendants()) do
if Part:IsA("BasePart") then
Part.Transparency = 0.5
Part.CanCollide = false
end
end
-- Rotation control
UserInputService.InputBegan:Connect(function(Key, GameProcessed)
if not GameProcessed and (Key.KeyCode == Enum.KeyCode.R or Key.KeyCode == Enum.KeyCode.ButtonX) then
RotatingObject = true
while RotatingObject do
wait()
RotationAmount += 2
end
end
end)
--Rotation control for mobile
script.Parent.RotateObject.MouseButton1Down:Connect(function()
if UserInputService.TouchEnabled and not UserInputService.MouseEnabled() then
RotatingObject = true
while RotatingObject do
wait()
RotationAmount += 2
end
end
end)
UserInputService.InputEnded:Connect(function(Key)
if Key.KeyCode == Enum.KeyCode.R then
RotatingObject = false
end
end)
-- Update object position and orientation during placement
RunService.RenderStepped:Connect(function()
if PlacingObject then
Mouse.TargetFilter = PreviewObject
if PreviewObject:FindFirstChild("MainPart") then
local ObjectCFrame = CFrame.new(Mouse.Hit.Position.X, Mouse.Hit.Position.Y + PreviewObject.PrimaryPart.Size.Y / 2, Mouse.Hit.Position.Z)
local ObjectAngles = CFrame.Angles(0, math.rad(RotationAmount), 0)
PreviewObject:SetPrimaryPartCFrame(ObjectCFrame * ObjectAngles)
end
end
end)
-- Finalize placement
Mouse.Button1Up:Connect(function()
if PlacingObject then
if UserInputService.KeyboardEnabled and UserInputService.MouseEnabled then
PlacingObject = false
PlacementEvent:FireServer(PreviewObject.Name, PreviewObject.PrimaryPart.CFrame)
Frame.Visible = true
PreviewObject:Destroy()
end
end
end)
--Placement Control for mobile
script.Parent.PlaceObject.MouseButton1Click:Connect(function()
if PlacingObject then
if UserInputService.TouchEnabled and not UserInputService.MouseEnabled() then
PlacingObject = false
PlacementEvent:FireServer(PreviewObject.Name, PreviewObject.PrimaryPart.CFrame)
Frame.Visible = true
PreviewObject:Destroy()
end
end
end)
end
end)
end
end
PlacementServer script (if needed, please note that texturing is no longer being used):
local PlacementEvent = game.ReplicatedStorage.PlacementEvent
local DestroyEvent = game.ReplicatedStorage:WaitForChild("DestroyEvent")
local RecolorEvent = game.ReplicatedStorage:WaitForChild("RecolorEvent")
local TextureEvent = game.ReplicatedStorage:WaitForChild("TextureEvent")
local ObjectFolder = game.ReplicatedStorage:WaitForChild("ObjectFolder")
local placedObjects = game.Workspace:WaitForChild("PlacedObjects")
-- Utility function to check if a position is within a part's bounds
local function isWithinBounds(boundsPart, position)
local size = boundsPart.Size
local cf = boundsPart.CFrame
local relativePos = cf:pointToObjectSpace(position)
return math.abs(relativePos.X) <= size.X / 2 and
math.abs(relativePos.Y) <= size.Y / 2 and
math.abs(relativePos.Z) <= size.Z / 2
end
PlacementEvent.OnServerEvent:Connect(function(Player, PreviewObject, ObjectCFrame)
-- Find the player's property by first searching the specific property types (e.g., SmallShop, LargeShop)
local propertiesFolder = game.Workspace.Properties:GetChildren() -- Get all property type folders (SmallShop, LargeShop, etc.)
local playerProperty = nil
for _, propertyType in ipairs(propertiesFolder) do
local properties = propertyType:GetChildren() -- Get all properties inside this type (e.g., SmallShop1, SmallShop2, etc.)
for _, property in ipairs(properties) do
local propertyInfo = property:FindFirstChild("PropertyInfo")
if propertyInfo then
local owner = propertyInfo:FindFirstChild("PropertyOwner")
if owner and owner.Value == Player then
playerProperty = property
break
end
end
end
if playerProperty then
break -- If we found the property, exit the loop
end
end
if not playerProperty then
warn("Player does not own a property or it could not be found!")
return
end
-- Check if the placement is within the player's property bounds
local propertyBounds = playerProperty:FindFirstChild("PropertyBounds")
if propertyBounds and isWithinBounds(propertyBounds, ObjectCFrame.Position) then
-- Clone and place the object
local Object = ObjectFolder:FindFirstChild(PreviewObject):Clone()
Object:SetPrimaryPartCFrame(ObjectCFrame)
local placedItemsFolder = playerProperty:FindFirstChild("PlacedItems")
if placedItemsFolder then
Object.Parent = placedItemsFolder
if Object:FindFirstChild("ObjectOwner") then
if Object.ObjectOwner.Value == nil then
Object.ObjectOwner.Value = Player
end
end
else
warn("Placed items folder not found in property!")
return
end
Object.ObjectOwner.Value = Player
else
warn("Placement outside of property bounds!")
end
end)
-- Handle object destruction
DestroyEvent.OnServerEvent:Connect(function(player, targetObject)
local ownedProperty = nil
-- Find the player's property
for _, propertyType in ipairs(game.Workspace.Properties:GetChildren()) do
local properties = propertyType:GetChildren()
for _, property in ipairs(properties) do
local propertyInfo = property:FindFirstChild("PropertyInfo")
if propertyInfo then
local owner = propertyInfo:FindFirstChild("PropertyOwner")
if owner and owner.Value == player then
ownedProperty = property
break
end
end
end
if ownedProperty then
break
end
end
-- Check if the targetObject is part of the ownedProperty's PlacedItems
if targetObject then --and ownedProperty then
local placedItemsFolder = ownedProperty:FindFirstChild("PlacedItems")
if placedItemsFolder and targetObject:IsDescendantOf(placedItemsFolder) then
local objectOwner = targetObject:FindFirstChild("ObjectOwner")
print(player.Name.." is player deleting!")
-- if objectOwner and objectOwner.Value == player then
targetObject:Destroy() -- Safe to destroy
ownedProperty = nil
--else
--warn("Player tried to destroy an object they do not own.")
--end
else
warn("Attempted to destroy an object not placed in owned property.")
end
end
end)
-- Handle object recoloring
RecolorEvent.OnServerEvent:Connect(function(player, targetObject, red, green, blue)
local ownedProperty = nil
-- Find the player's property
for _, propertyType in ipairs(game.Workspace.Properties:GetChildren()) do
local properties = propertyType:GetChildren()
for _, property in ipairs(properties) do
local propertyInfo = property:FindFirstChild("PropertyInfo")
if propertyInfo then
local owner = propertyInfo:FindFirstChild("PropertyOwner")
if owner and owner.Value == player then
ownedProperty = property
break
end
end
end
if ownedProperty then
break
end
end
-- Check if the targetObject is part of the ownedProperty's PlacedItems
if targetObject then --and ownedProperty then
local placedItemsFolder = ownedProperty:FindFirstChild("PlacedItems")
if placedItemsFolder and targetObject:IsDescendantOf(placedItemsFolder) then
local objectOwner = targetObject:FindFirstChild("ObjectOwner")
--if objectOwner and objectOwner.Value == player then
-- Recolor logic
local r = tonumber(red)
local g = tonumber(green)
local b = tonumber(blue)
if r and g and b then
local newColor = Color3.fromRGB(r, g, b)
for _, part in ipairs(targetObject:GetDescendants()) do
if part:IsA("BasePart") then
part.BrickColor = BrickColor.new(newColor)
end
end
else
warn("Invalid RGB values provided")
end
else
warn("Player tried to recolor an object they do not own.")
end
--else
--warn("Attempted to recolor an object not placed in owned property.")
----end
end
end)
--end)
-- Handle object texturing
TextureEvent.OnServerEvent:Connect(function(player, targetObject, selectedMaterial)
if targetObject and targetObject:IsDescendantOf(placedObjects) then
local parentModel = targetObject:FindFirstAncestorOfClass("Model")
local objectOwner = parentModel:WaitForChild("ObjectOwner")
if objectOwner.Value == player then
for _, part in ipairs(parentModel:GetDescendants()) do
if part:IsA("Part") then
part.Material = Enum.Material[selectedMaterial]
end
end
end
end
end)
Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.