You can write your topic however you want, but you need to answer these questions:
What do you want to achieve? I want to create a “grid” building system, where blocks can be placed. [example in gyazo video linked below]
What is the issue? The system “works” but only in certain directions. Not to mention that the blocks will be placed INSIDE of a lower block sometimes.
What solutions have you tried so far? I have tried changing the snap() function, increasing the (0.5) to a (0.6) which fixed the problem, but only in the Y direction, so I changed it back. I tried changing the (0.5) values between each of them, but am still having trouble.
local mouse = game.Players.LocalPlayer:GetMouse()
local tool = script.Parent
toolEquipped = false
recentlyMoved = false
local posx
local posy
local posz
local blocks = {
test = game.ReplicatedStorage.Blocks.BlockTemplate
}
mouse.TargetFilter = blocks.test
local gridSize = 4
local function snap()
posx = math.floor(mouse.Hit.X / gridSize + 0.5) * gridSize
posy = math.floor(mouse.Hit.Y / gridSize + 0.5) * gridSize
posz = math.floor(mouse.Hit.Z / gridSize + 0.5) * gridSize
end
tool.Equipped:Connect(function()
toolEquipped = true
snap()
local pos1 = Vector3.new(posx, posy, posz)
blocks.test.Parent = workspace
blocks.test.Position = pos1
mouse.Move:Connect(function()
if toolEquipped == true then
snap()
posV = Vector3.new(posx, posy, posz)
blocks.test.Parent = workspace
blocks.test.Position = posV
print("moved")
end
end)
mouse.Button1Down:Connect(function()
local a = blocks.test:Clone()
a.Parent = workspace
a.Position = posV
a.Transparency = 0
a.CanCollide = true
end)
end)
tool.Unequipped:Connect(function()
toolEquipped = false
blocks.test.Parent = game.ReplicatedStorage.Blocks
end)
If you’re looking to create something like Minecraft’s building system, couldn’t you just take the mouse’s target’s position, then add the block’s size times the mouse’s normal?
Here’s what I wrote quickly:
local players = game:GetService('Players')
local localPlayer: Player = players.LocalPlayer
local mouse = localPlayer:GetMouse()
local userInputService = game:GetService('UserInputService')
local part = Instance.new('Part')
part.Transparency = 0.5
part.CanCollide = false
part.Anchored = true
part.Parent = workspace
part.Size = Vector3.new(4,4,4)
local filter = {
part
}
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = filter
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
mouse.Move:Connect(function()
local raycastResult = workspace:Raycast(mouse.UnitRay.Origin, mouse.UnitRay.Direction * 1000, raycastParams)
if raycastResult then
part.Position = (raycastResult.Instance.Position) + (part.Size * raycastResult.Normal)
end
end)
userInputService.InputBegan:Connect(function(input: InputObject)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
local clonedPart = part:Clone()
clonedPart.Parent = workspace
clonedPart.CanCollide = true
clonedPart.Transparency = 0
end
end)
The problem with this is that, while it does what you show in the video, if I put my mouse over a larger part than the block size, the block goes to the center of that part instead of where my mouse is
So basically when you use workspace:Raycast(), it returns an object called “RaycastResult”.
So, when we cast our ray, raycastResult is our result:
local raycastResult = workspace:Raycast(mouse.UnitRay.Origin, mouse.UnitRay.Direction * 1000, raycastParams)
So, take the raycastResult’s Instance property and check if it has some attribute, like an easily identifiable name
if raycastResult.Name == 'Block' then
part.Position = (raycastResult.Instance.Position) + (part.Size * raycastResult.Normal)
else
-- if the mouse's instance isn't a block, then snap it to the grid.
-- here is where you'd snap it
end
Ohh my bad, basically you’d just do something like math.round(x * 4) / 4 where x is your position on the axis and 4 would be replaced with whatever your grid size is.
Still having trouble, but im going to try to troubleshoot the code to see what is going on. I do not understand normals probably as I should, but reading your code has given me an idea of what I need to do. If I need some help still, I’ll reply here
I have determined (through the great debug tool known as PRINT) that for some reason, the X and Z (yes, i changed LookVector to RightVector and it starts bugging out) values do not change on two of the three values (I have edited the Y one to suit me, as it works like it should as I have it) if I am holding my mouse over a part that is not “blocktemplate”
mouse.Move:Connect(function()
if toolEquipped == true then
snap()
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
raycastParams.FilterDescendantsInstances = {blocks.test, tool, player.Character}
local raycastResult = workspace:Raycast(mouse.UnitRay.Origin, mouse.UnitRay.Direction * 300, raycastParams)
if raycastResult.Instance.Name == "BlockTemplate" then
posV = (raycastResult.Instance.Position) + (blocks.test.Size * raycastResult.Normal)
blocks.test.Position = posV
else
if raycastResult.Normal == raycastResult.Instance.CFrame.RightVector or raycastResult == -raycastResult.Instance.CFrame.RightVector then
local posVX = ((raycastResult.Instance.Position) + (blocks.test.Size * raycastResult.Normal)).X
local posVY = math.round(posy * gridSize) / gridSize
local posVZ = math.round(posz * gridSize) / gridSize
posV = Vector3.new(posVX, posVY, posVZ)
blocks.test.Position = posV
print(posV, "1")
elseif raycastResult.Normal == raycastResult.Instance.CFrame.UpVector or raycastResult == -raycastResult.Instance.CFrame.UpVector then
local posVX = math.round(posx * gridSize) / gridSize
local posVY = math.round(posy * gridSize) / gridSize + 4--((raycastResult.Instance.Position) + (blocks.test.Size * raycastResult.Normal)).Y
local posVZ = math.round(posz * gridSize) / gridSize
posV = Vector3.new(posVX, posVY, posVZ)
blocks.test.Position = posV
print(posV, "2")
elseif raycastResult.Normal == raycastResult.Instance.CFrame.LookVector or raycastResult == -raycastResult.Instance.CFrame.LookVector then
local posVX = math.round(posx * gridSize) / gridSize
local posVY = math.round(posy * gridSize) / gridSize
local posVZ = ((raycastResult.Instance.Position) + (blocks.test.Size * raycastResult.Normal)).Z
posV = Vector3.new(posVX, posVY, posVZ)
blocks.test.Position = posV
print(posV, "3")
end
end
end
end)
For some reason, in
if raycastResult.Normal == raycastResult.Instance.CFrame.RightVector or raycastResult == -raycastResult.Instance.CFrame.RightVector then
local posVX = ((raycastResult.Instance.Position) + (blocks.test.Size * raycastResult.Normal)).X
local posVY = math.round(posy * gridSize) / gridSize
local posVZ = math.round(posz * gridSize) / gridSize
posV = Vector3.new(posVX, posVY, posVZ)
blocks.test.Position = posV
print(posV, "1")
the X value does not change if I hold my mouse over a part that is not “blocktemplate” (It is the same with the Z part, the Z value does not change if I hold my mouse over a part that is not “blocktemplate”)
Ah shoot, I think it’s because I forgot to add the .Normal property when checking the negative direction vectors, so switch raycastResult == with raycastResult.Normal ==
Weird, I’ll try it out tomorrow and let you know if I come up with anything. Send me a pm in like 16ish hours if nobody else responds or if you don’t come up with a solution as I might forget
mouse.Move:Connect(function()
if toolEquipped == true then
snap()
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
raycastParams.FilterDescendantsInstances = {blocks.test, tool, player.Character}
local raycastResult = workspace:Raycast(mouse.UnitRay.Origin, mouse.UnitRay.Direction * 300, raycastParams)
if raycastResult.Instance.Name == "Wood" then
posV = (raycastResult.Instance.Position) + (blocks.test.Size * raycastResult.Normal)
blocks.test.Position = posV
else
if raycastResult.Normal == raycastResult.Instance.CFrame.LookVector then
local posVX = math.round(posx * gridSize) / gridSize
local posVY = math.round(posy * gridSize) / gridSize
local posVZ = math.round(posz * gridSize) / gridSize - 4
posV = Vector3.new(posVX, posVY, posVZ)
blocks.test.Position = posV
elseif raycastResult.Normal == -raycastResult.Instance.CFrame.LookVector then
local posVX = math.round(posx * gridSize) / gridSize
local posVY = math.round(posy * gridSize) / gridSize
local posVZ = math.round(posz * gridSize) / gridSize
posV = Vector3.new(posVX, posVY, posVZ)
blocks.test.Position = posV
elseif raycastResult.Normal == raycastResult.Instance.CFrame.UpVector or raycastResult.Normal == -raycastResult.Instance.CFrame.UpVector then
local posVX = math.round(posx * gridSize) / gridSize
local posVY = math.round(posy * gridSize) / gridSize + 4--((raycastResult.Instance.Position) + (blocks.test.Size * raycastResult.Normal)).Y
local posVZ = math.round(posz * gridSize) / gridSize
posV = Vector3.new(posVX, posVY, posVZ)
blocks.test.Position = posV
elseif raycastResult.Normal == raycastResult.Instance.CFrame.RightVector then
local posVX = math.round(posx * gridSize) / gridSize - 4
local posVY = math.round(posy * gridSize) / gridSize
local posVZ = math.round(posz * gridSize) / gridSize
posV = Vector3.new(posVX, posVY, posVZ)
blocks.test.Position = posV
elseif raycastResult.Normal == -raycastResult.Instance.CFrame.RightVector then
local posVX = math.round(posx * gridSize) / gridSize
local posVY = math.round(posy * gridSize) / gridSize
local posVZ = math.round(posz * gridSize) / gridSize
posV = Vector3.new(posVX, posVY, posVZ)
blocks.test.Position = posV
end
end
end
end)
If you can’t tell, I solved the problem by separating the “or” statements that you made into their own “if” statements, which allowed me to add or subtract the correct gridsize amount for every axis, so blocks dont get placed inside of terrain and are placed where I want them to be.
Special thanks to: you, cody/7z99 for helping me get this far in the first place
&
kingerman88 for helping me realize this