We can’t definitively figure out how MeepCity does this, but I would guess he’s using the surface normal argument provided by Workspace’s raycasting functions to rotate the resulting CFrame
^ It is really simple if you know what a surface normal is and how to raycast. There is an example on the wiki of making a crate placement system with cross product or you can just rotate it afterward or use CFrame.new(pos,pos+norm) or whatever
local mouse = game:GetService("Players").LocalPlayer:GetMouse();
local moveModel = game:GetService("Workspace"):WaitForChild("Wood Crate");
-- don't detect the model we're moving
mouse.TargetFilter = moveModel;
function placeAtMouse()
-- make sure the mouse is pointing at something
if mouse.Target then
-- where we cast our ray from (shifted slightly up so the ray will hit the surface we're hovering)
local origin = mouse.Hit.p + Vector3.new(0, 0.1, 0);
-- cast our ray and get our normal, which is a unit vector
local ray = Ray.new(origin, Vector3.new(0, -1, 0));
local hit, pos, normal = game:GetService("Workspace"):FindPartOnRay(ray, moveModel);
moveModel:SetPrimaryPartCFrame(CFrame.new(pos,pos+normal*15) * CFrame.Angles(-math.rad(90),0,0))
print (moveModel:GetPrimaryPartCFrame().p)
end;
end;
-- check every frame
game:GetService("RunService").RenderStepped:connect(function()
placeAtMouse();
end);
Explain me the weird vector 3 printing when mouse is on the sides of the big part (say wall)
It seriously dissapears when pointing to ‘walls’
Because the ray is not going toward any walls… why are you not shooting the ray in the direction of the mouse from the camera??? a ray straight down isnt supposed to hit vertical walls.
local mouse = game:GetService("Players").LocalPlayer:GetMouse();
local moveModel = game:GetService("Workspace"):WaitForChild("Wood Crate");
-- don't detect the model we're moving
mouse.TargetFilter = moveModel;
function placeAtMouse()
-- make sure the mouse is pointing at something
if mouse.Target then
-- cast our ray and get our normal, which is a unit vector
local unitRay = workspace.CurrentCamera:ScreenPointToRay(mouse.X, mouse.Y, 1) --origin, Vector3.new(0, -1, 0));
local ray = Ray.new(unitRay.Origin, unitRay.Direction * 1000)
local hit, pos, normal = game:GetService("Workspace"):FindPartOnRay(ray, moveModel); print (normal)
moveModel:SetPrimaryPartCFrame(CFrame.new(pos,pos+normal*15) * CFrame.Angles(-math.rad(90),0,0))
end;
end;
-- check every frame
game:GetService("RunService").RenderStepped:connect(function()
placeAtMouse();
end);
Sorry I’m late to the party! There is a little known Dragger service to allow the easy implementation of dragger systems. This is the service used by the RbxStamperlibrary. Why write your own version when you could use a tried and tested service?
A game I’m working on uses a similar placement system to the one in your video. I’ve adapted your code to use the same math as mine, hopefully it’s helpful.
local mouse = game:GetService("Players").LocalPlayer:GetMouse();
local moveModel = game:GetService("Workspace"):WaitForChild("Wood Crate");
local player = game.Players.LocalPlayer
-- don't detect the model we're moving
mouse.TargetFilter = moveModel;
local rotation = 0 -- in radians
function placeAtMouse()
-- make sure the mouse is pointing at something
if mouse.Target then
-- where we cast our ray from (shifted slightly up so the ray will hit the surface we're hovering)
local origin = mouse.Hit.p + Vector3.new(0, 0.1, 0);
-- cast our ray and get our normal, which is a unit vector
local unitRay = workspace.CurrentCamera:ScreenPointToRay(mouse.X, mouse.Y, 1) --origin, Vector3.new(0, -1, 0));
local ray = Ray.new(unitRay.Origin, unitRay.Direction * 1000)
local hit, pos, normal = workspace:FindPartOnRay(ray, moveModel)
--============================= New code I've added =============================--
local front = normal
local crossVector = (normal.Y > 0.8) and (pos-player.Character.HumanoidRootPart.Position).Unit or Vector3.new(0,1,0)
local right = front:Cross(crossVector)
local top = front:Cross(-right)
local back = -front
local cf = CFrame.new(pos.X, pos.Y, pos.Z, right.X, top.X, back.X, right.Y, top.Y, back.Y, right.Z, top.Z, back.Z)
moveModel:SetPrimaryPartCFrame(cf * CFrame.Angles(0,0,rotation))
end;
end;
-- check every frame
game:GetService("RunService").RenderStepped:connect(function()
placeAtMouse();
end);
One difference is that I choose to rotate the object towards the player when placing an object on a horizontal surface, but this is easy to adjust.
To indicate your problem as solved, click the checkbox icon on the bottom of the post that solved it (near the like button) and the topic won’t attract new answers. It also recognizes their effort by marking it as the solution.
I know you did this a really long time ago, but what I was just wondering(if you remember) what the use of multiplying by 15 was in the line : moveModel:SetPrimaryPartCFrame(CFrame.new(pos,pos+normal*15) * CFrame.Angles(-math.rad(90),0,0))
This is very late, but I got the code working, but how would I snap this to a grid. And also, show the entire model when placing instead of leaking half of it into the wall. This is confusing for me because you didn’t use Mouse.Hit anywhere inside the code.