Sorry if I’m missing something, but how should I modify your code to fit mine?
Try this
script.Parent.Activated:connect(function()
if (game.ReplicatedStorage.block.Handle- cursor.hit.p).magnitude <= 15) then
if cursor.Target.Position.X + 1.5 == cursor.Hit.X then
X = math.ceil(cursor.Hit.X) -(math.ceil(cursor.Hit.X) % 3) + 3
else
X = math.ceil(cursor.Hit.X) -(math.ceil(cursor.Hit.X) % 3)
end
if ((cursor.Hit.Y%0.5) == 0) then
if cursor.Target.Position.Y + 1.5 == cursor.Hit.Y then
Y = math.ceil(cursor.Hit.Y) - (math.ceil(cursor.Hit.Y) % 3) + 3
else
Y = math.ceil(cursor.Hit.Y) - (math.ceil(cursor.Hit.Y) % 3)
end
else
Y = math.ceil(cursor.Hit.Y) - (math.ceil(cursor.Hit.Y) % 3)
end
if cursor.Target.Position.Z + 1.5 == cursor.Hit.Z then
Z = math.ceil(cursor.Hit.Z) -(math.ceil(cursor.Hit.Z) % 3) + 3
else
Z = math.ceil(cursor.Hit.Z) -(math.ceil(cursor.Hit.Z) % 3)
end
end
end
end)
this might be wrong,because its been awhile since i did something like this
Just a quick fix, you have to distribute X
, Y
, and Z
coordinates for each component of the new Vector3
:
local function round(vector,grid)
return Vector3.new(
math.floor(vector.X/grid+.5)*grid,
math.floor(vector.Y/grid+.5)*grid,
math.floor(vector.Z/grid+.5)*grid
)
end
round(mouse.Hit.Position,3)
Thank you! That fixed it, it’s definitely a nice, simple solution.
Actually, this doesn’t work on two sides of the block. It places the block inside of the block I am pointing at.
https://gyazo.com/e4882c9a22bda629429caf14084316b8
Try getting the normal from mouse.Hit
using a ray and adding that to the original position:
-- services
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local player = Players.LocalPlayer
local block = ReplicatedStorage:WaitForChild("block")
-- how far you want the player to reach
local REACH = 100
local castParams = RaycastParams.new()
mouse.Button2Down:Connect(function()
local unitRay = mouse.UnitRay
castParams.FilterDescendantsInstances = { player.Character }
-- automatically ignores the player's character
local result = Workspace:Raycast(
unitRay.Origin, unitRay.Direction * REACH,
castParams
)
if result then
local newBlock = block:Clone()
newBlock.Position = round(result.Position + result.Normal * 1.5, 3)
newBlock.Parent = Workspace
end
end)
It works, finally! Thank you very much.
Sorry for bumping, but is this a local script or normal? And where would I put this?
I’m making a similar system, but I don’t fully understand the Raycasting. Could you please explain how it works?
This should be a local script, but if you want everyone to see the blocks you place, you should fire a remote to the server instead of creating the block client-side.
@Smartysaur11, if you’re talking about how Workspace:Raycast
works, you can look at the Intro to Raycasting tutorial on the dev hub or check the API reference.
Ty raycasting has always been one of the things that stumps me
Sorry to stir upp this again but im working on a similiar problem and am interested.
So, the round()
function. What is that? I searched up ‘round() roblox’ and the best result I got was a function returning math.floor(n + 0.5)
where n
is the number. But here we are passing n as a vector3??? That screws with my head. To add to that what is the second parameter for? To round it to multiples of 3? If so how would that function look?
Thanks!
If you look at some of the replies before my solution, you’ll see the round()
function there.
And you guessed correctly on the second parameter
I guess I would rename the function to something like snapToGrid()
? That could clear the confusion.
Sorry, I’m a little dumb, but
Why are you multiplying the result’s normal with 1.5? Is it like (studs / 2)
?
Sorry for bumping, but how would a Prediction Block system be made with this?
(EDIT: I’m still learning scripting btw)
It’s the exact same way. Just make a clone which will be the prediction block and make it transparent.
I mean something like BedWars or SkyWars where when you look in front of you, you can see a transparent block.
I made this for my game.
It’s a bit buggy but it does the job done.
Place a Local Script in a Tool.
Place a Normal script in the tool.
Place a Folder named “PartsFolder” in the workspace and place all the parts you want in the map.
Place a Folder named “PartsPreview” in the workspace for the prediction block.
Place a Remote event named “PlaceBlock” in the tool
This is the code for the Local Script:
local PlaceBlockEvent = script.Parent.PlaceBlock
local equipped = false
local uis = game:GetService("UserInputService")
local mouse = game.Players.LocalPlayer:GetMouse()
local character = game.Players.LocalPlayer.Character or game.Players.LocalPlayer.CharacterAdded:Wait()
local cam = workspace.CurrentCamera
local function findClosestPart(preview)
local lowestMagParts = {}
local currentPart = nil
local currentClosestDir = nil
local currentClsoestmag = nil
for _,v in pairs(game.Workspace.PartsFolder:GetDescendants()) do
if v:IsA("BasePart") then
local magnitude1 = (character.PrimaryPart.Position - v.Position).Magnitude
if magnitude1 <= 25 then
table.insert(lowestMagParts, v)
end
end
end
local lowestMagnitude = nil
for _,v in pairs(lowestMagParts) do
if v:IsA("BasePart") then
local mouse3DPos = character.PrimaryPart.Position + (mouse.Hit.Position - character.PrimaryPart.Position).Unit * (character.PrimaryPart.Position - v.Position).Magnitude
local mag = (v.Position - mouse3DPos).Magnitude
if not lowestMagnitude then
lowestMagnitude = mag
currentPart = v
continue
end
if mag < lowestMagnitude then
lowestMagnitude = mag
currentPart = v
end
end
end
local surfaceIDs = {
top = currentPart.Position + Vector3.new(0,25,0),
bottom = currentPart.Position - Vector3.new(0,25,0),
left = currentPart.Position - Vector3.new(25,0,0),
right = currentPart.Position + Vector3.new(25,0,0),
front = currentPart.Position - Vector3.new(0,0,25),
back = currentPart.Position + Vector3.new(0,0,25),
}
for surfaceId, pos in pairs(surfaceIDs) do
local mag = ((character.PrimaryPart.Position + (mouse.Hit.Position - character.PrimaryPart.Position).Unit * 25) - pos).Magnitude
if not currentClsoestmag then
currentClsoestmag = mag
currentClosestDir = surfaceId
continue
end
if mag < currentClsoestmag then
currentClsoestmag = mag
currentClosestDir = surfaceId
end
end
if currentClosestDir ~= nil then
table.clear(lowestMagParts)
return currentPart, currentClosestDir
end
end
local connection = nil
script.Parent.Equipped:Connect(function()
if equipped == false then
equipped = true
local clonePreview = Instance.new("Part")
clonePreview.Parent = game.Workspace:WaitForChild("PartsPreview")
clonePreview.Name = script.Parent.Parent.Name.."Preview"
clonePreview.Transparency = 1
clonePreview.Size = Vector3.new(3,3,3)
clonePreview.CanCollide = false
clonePreview.Anchored = true
local selection = Instance.new("SelectionBox", clonePreview)
selection.Visible = true
selection.Adornee = clonePreview
selection.LineThickness = 0.05
selection.Color3 = Color3.fromRGB(0,0,0)
for _,v in pairs(game.Workspace.PartsPreview:GetChildren()) do
if v ~= clonePreview then
v:Destroy()
end
end
mouse.TargetFilter = clonePreview
task.defer(function()
while wait() do
if equipped == true then
local mouseX,mouseY,mouseZ = mouse.Hit.X, mouse.Hit.Y, mouse.Hit.Z
local mouseTarget = mouse.TargetSurface
if mouse.Target ~= nil then
if mouseTarget == Enum.NormalId.Top then
clonePreview.CFrame = (CFrame.new(mouse.Target.Position + Vector3.new(0,3, 0))) --- the 3 is the size of the block
elseif mouseTarget == Enum.NormalId.Bottom then
clonePreview.CFrame = (CFrame.new(mouse.Target.Position - Vector3.new(0,3, 0))) --- the 3 is the size of the block
elseif mouseTarget == Enum.NormalId.Left then
clonePreview.CFrame = (CFrame.new(mouse.Target.Position - Vector3.new(3, 0,0))) --- the 3 is the size of the block
elseif mouseTarget == Enum.NormalId.Right then
clonePreview.CFrame = (CFrame.new(mouse.Target.Position + Vector3.new(3,0, 0))) --- the 3 is the size of the block
elseif mouseTarget == Enum.NormalId.Front then
clonePreview.CFrame = (CFrame.new(mouse.Target.Position - Vector3.new(0,0, 3))) --- the 3 is the size of the block
elseif mouseTarget == Enum.NormalId.Back then
clonePreview.CFrame = (CFrame.new(mouse.Target.Position + Vector3.new(0,0, 3))) --- the 3 is the size of the block
end
elseif mouse.Target == nil then
local closestPart, direction = findClosestPart(clonePreview)
if tostring(direction) == "top" then
clonePreview.CFrame = (CFrame.new(closestPart.Position + Vector3.new(0,3, 0))) --- the 3 is the size of the block
elseif tostring(direction) == "bottom" then
clonePreview.CFrame = (CFrame.new(closestPart.Position - Vector3.new(0,3, 0))) --- the 3 is the size of the block
elseif tostring(direction) == "left" then
clonePreview.CFrame = (CFrame.new(closestPart.Position - Vector3.new(3,0,0))) --- the 3 is the size of the block
elseif tostring(direction) == "right" then
clonePreview.CFrame = (CFrame.new(closestPart.Position + Vector3.new(3,0, 0))) --- the 3 is the size of the block
elseif tostring(direction) == "front" then
clonePreview.CFrame = (CFrame.new(closestPart.Position - Vector3.new(0,0, 3))) --- the 3 is the size of the block
elseif tostring(direction) == "back" then
clonePreview.CFrame = (CFrame.new(closestPart.Position + Vector3.new(0,0, 3))) --- the 3 is the size of the block
end
closestPart = nil
direction = nil
end
end
end
end)
connection = mouse.Button1Down:Connect(function()
if equipped == true then
for _,v in pairs(game.Workspace:WaitForChild("PartsFolder"):GetDescendants()) do
if v:IsA("BasePart") then
if clonePreview.Position == v.Position then
return
end
end
end
local primaryPart = clonePreview.CFrame
PlaceBlockEvent:FireServer(primaryPart)
end
end)
end
end)
script.Parent.Unequipped:Connect(function()
if equipped == true then
equipped = false
if workspace.PartsPreview:FindFirstChild(script.Parent.Parent.Name.."Preview") then
local preview = workspace.PartsPreview:FindFirstChild(script.Parent.Parent.Name.."Preview")
preview:Destroy()
end
end
connection:Disconnect()
connection = nil
end)
Here is the code for the Normal script:
local event = script.Parent.PlaceBlock
local part = game.ReplicatedStorage:WaitForChild("Wood Plank") ---- name of the part in the replicated storage you want to clone for the block placement system
event.OnServerEvent:Connect(function(plr, pos)
local Clone = part:Clone()
Clone.Parent = game.Workspace:WaitForChild("PartsFolder")
Clone.CFrame = pos
end)
This is my first time making a reply so let me know if you have problems!
Also here’s a video clip of the tool. It’s a bit lag since I’m on a laptop
robloxapp-20220302-2138143.wmv (3.4 MB)
Thank you so much for your help! I’ve been looking around for something like this.
However, it does not seem to work. There is currently nothing in the output.
I’ve followed your directions and re-checked the script…
EDIT: Fixed the problem, i didnt have a handle lol
However, for some odd reason, I get this error.
Here’s where it’s causing the issue.
It seems to be happening when I take the mouse off the map.
Thanks anyways, you are a lifesaver!