I’ve been working on a grab mechanic for a future game where players can pick up and throw props around and interact with the environment/solve puzzles using physics. It’s pretty bare-bones at this point, but I’ve thrown together a few clips of my progress so far.
I put an attachment inside of each grabbable part, and an AlignPosition is applied to them to place them in front of the player’s camera when they pick them up:
-- LocalScript Magic
local grabParams = function() : RaycastParams
local newParams = RaycastParams.new()
newParams.FilterType = Enum.RaycastFilterType.Include
newParams.FilterDescendantsInstances = CollectionSVC:GetTagged("PhysObject")
return newParams
end
local function GrabCast(camCF : CFrame)
return workspace:Raycast(camCF.Position, camCF.LookVector*6.5, grabParams())
end
mouse.Button1Down:Connect(function()
local camCF = camera.CFrame
local grab = GrabCast(camCF)
if not grabObj and not grab then return end
-- grabObj is whatever part is being held, if any
Events.GrabEvent:FireServer(grabObj or grab.Instance, grabObj and 1 or 0)
-- allows the server to change NetworkOwnership of picked up/dropped parts
grabObj = (grab and not grabObj) and grab.Instance or nil
-- determines whether the click was to pick up a part or drop a held one
grabber.Parent = grabObj or ReplicatedStorage
grabber.Attachment0 = grabObj and grabObj:FindFirstChild("GrabAttach") or nil
end)
local NewHighlighter = function() : Highlight
local highlight = Instance.new("Highlight", ReplicatedStorage)
highlight.Name = "GrabHighlight"
return highlight
end
local highlighter = NewHighlighter()
RunSVC.Heartbeat:Connect(function(delta)
local camCF = camera.CFrame
grabber.Position = camCF.Position + (camCF.LookVector*6)
local grab = GrabCast(camCF)
if not highlighter then
highlighter = NewHighlighter()
end
-- In case a held part gets deleted along with the highlight for whatever reason
highlighter.Parent = (grab and not grabObj) and grab.Instance or ReplicatedStorage
highlighter.Adornee = (grab and not grabObj) and grab.Instance or nil
end)
-- ServerScript
grabEvent.OnServerEvent:Connect(function(player, grabObject : BasePart, state : number)
grabObject:SetAttribute("CanGrab", state == 1)
-- makes sure no other player can grab a part while it's being held
grabObject:SetNetworkOwner(state == 0 and player or nil)
-- decides ownership based on whether part is being picked up or dropped
end)
Players can also grab parts attached to others, like this turning gear:
-- How the display numbers are calculated [ServerScript]
local RunSVC = game:GetService("RunService")
local demoModel = script.Parent
local displayGui = demoModel.TV.Screen:WaitForChild("Display", 5)
local gear = demoModel:WaitForChild("Gear1", 5)
RunSVC.Heartbeat:Connect(function(delta)
for i,label : TextLabel in ipairs(displayGui:GetChildren()) do
label.Text = math.floor(math.deg(-gear.CFrame.UpVector.Z)%360).."°"
end
end)
I also made a light that checks the length of the spring attached to the switch to turn the bulb on and off:
-- How the bulb works [ServerScript]
local bulb = script.Parent
local spring = bulb:FindFirstChild("SpringConstraint") :: SpringConstraint
local light = bulb:FindFirstChild("PointLight") :: PointLight
local on = false
while task.wait(.1) do
if spring.CurrentLength < 10 then continue end
on = not on
bulb.BrickColor = BrickColor.new(on and "Cool yellow" or "Really black")
light.Enabled = on
repeat task.wait(.1) until spring.CurrentLength < 10
end
As well as this scale that’s held up by a few different constraints:
There’s no script attached to it, it’s just the constraints at this point. I could use the length of the spring to come up with some numbers for a weight puzzle in the future.
I thought these were neat little things to do with some of the constraints Roblox has available. I’ll keep fiddling with this, and maybe provide some updates on what I come up with in the future.
If anyone has any ideas for what to do with this stuff, let me know, I’d appreciate it