Problem with getting part to properly position, Help!

Basically i am trying to make a script that allows the player to hold an item as in games like Amnesia,
However i have one problem. The part does not stay infront of the player, it’s really wonky:

https://gyazo.com/4058e178626587d826c01230315a2c27

https://gyazo.com/80994510afce6680798c0ec0b9b42253
(Note: the game will be played in first person)

This is my script so far:

local plr = game.Players.LocalPlayer
local mouse = plr:GetMouse()
local grabbing = false
local grabitem = nil
local dist = 4

local function calculatePos(angle)
	local distZ = dist * angle
	local distY = math.sqrt(dist^2 - distZ^2) 
	
	--print(distZ)
	--print(distY)
	
	return plr.Character.PrimaryPart.CFrame:ToWorldSpace(CFrame.new(0, -distY, -distZ))
end

mouse.Button1Down:Connect(function()
	local brick = mouse.target
	if brick:FindFirstChild("ID") then
		grabbing = true
		grabitem = brick
		grabitem.Anchored = true
		grabitem.CanCollide = false
	end
end)

mouse.Button1Up:Connect(function()
	if grabbing == true then 
		grabbing = false 
		grabitem.Anchored = false
		grabitem.CanCollide = true
	end
end)

while wait() do
	if grabbing == true then
		for i = 0, 1, 0.05 do
			wait()
			local opp = (plr.Character.PrimaryPart.Position.Y - mouse.hit.p.Y) 
			--print(opp)
			local adj = (mouse.hit.p - Vector3.new(plr.Character.PrimaryPart.Position.X, mouse.hit.p.Y, plr.Character.PrimaryPart.Position.Z)).magnitude
			local hyp = math.sqrt(opp^2 + adj^2) 
			local angle = math.acos(adj / hyp)
			
			local newCframe = grabitem.CFrame:Lerp(calculatePos(angle), i)
			grabitem.CFrame = newCframe
			grabitem.CFrame = CFrame.new(grabitem.Position, plr.Character.PrimaryPart.Position)
		end
	end
end
1 Like

If I were to create a system like this, I would probably use body movers instead of CFrame so the objects don’t clip through the floor.

We can simply set a BodyPosition’s position to the camera’s lookVector CFrame multiplied by a “radius” CFrame, or distance away from the characters camera. We can do this on InputChanged, which fires when the mouse is moved, so no unnecessary loop.

Then, you can use a BodyGyro to face the object at the players camera. You will need to update the BodyGyro’s CFrame relative to the camera’s CFrame on InputChanged as well. You can then change the gyro’s CFrame rotation if the player wants to rotate it midair with key combos like SHIFT+W/A/S/D.

If you don’t want object collisions, you can just set the objects to CanCollide false. BodyPosition and Gyro also have built in “lerping”!

1 Like

What you currently have going on is nice, but with all these calculations it’s hard to understand what’s wrong.
What I propose is another approach to this:

local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local camera = workspace.CurrentCamera

local function pos(dist)
    return camera.CFrame.Position + (mouse.UnitRay.Direction * dist)
end

Here is the explanation for this.

And since this is gonna be a first person thing, thus the camera’s cframe is inside the head all is good.

The only problem is the rotation part which is not being counted for, which I guess is simple and can easily be fixed by rotating the part to the camera’s position, and since the camera in first person is inside of the head, the object would be looking at the character

@starmaq @KeysOfFate i have done both your suggestions, the body position & gyro is a good idea i don’t know why i didnt think of it. However when i try use your code starmaq it kinda bugs out again:
https://gyazo.com/16a68d2558ad70a60a99702fe189f674

new code:

local plr = game.Players.LocalPlayer
local char = plr.Character
local grabbing = false
local grabbeditem = nil
local dist = 4
local userinputservice = game:GetService('UserInputService')
local mouse = plr:GetMouse()
local camera = workspace.CurrentCamera

mouse.Button1Down:Connect(function()
	if mouse.Target and mouse.Target.Locked then
		print("Block")
		grabbing = true
		grabbeditem = mouse.Target
		local bp = Instance.new("BodyPosition", grabbeditem)
		bp.D = 150
	end
end)

mouse.Button1Up:Connect(function()
	grabbing = false
	if grabbeditem and grabbeditem:FindFirstChild("BodyPosition") then
		grabbeditem.BodyPosition:Destroy()
	end
end)

userinputservice.InputBegan:Connect(function(input, gameProcessedEvent)
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		if grabbing == true then
    		grabbeditem.BodyPosition.Position = camera.CFrame * (mouse.UnitRay.Direction * dist)
		end	
	end
end)

Yeah I did some mistakes, try the new code that I posted, it will result to a Vector3 not a CFrame

Tried the new code and again, the same problem occurs again:
https://gyazo.com/5c99e513c7edddf867b1ff59c98359db

I mean you can technically do what @KeysOfFate proposed in the first place, utilising the fact that this is in first person, and position the part in the direction of the Camera/Mouse (in this case they’re same thing) and make it go further out a bit.

CFrame.new(camera.CFrame.Position + Vector.new(4,0,0), camera.CFrame)

Try replacing the InputBegan code with this:

userinputservice.InputChanged:Connect(function(input, gameProcessedEvent)
	if not gameProcessedEvent then
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			if grabbing == true then
				grabbeditem.BodyPosition.Position = (CFrame.new(camera.CFrame.Position,camera.CFrame.LookVector)* CFrame.new(dist,0,0)).Position
			end	
		end
	end
end)

edit: cut off the top whoops!

It’s working better however it isn’t infront of the player, it’s not relative. would i be able to apply :ToWorldSpace to bodypositions?

Post a screenshot, it should be relative. If its not directly in front of the player, the offset axis might be wrong.

https://gyazo.com/d595e99beef669d273474fe7606e3150
It doesn’t follow the mouse at all either

and as you said:

local plr = game.Players.LocalPlayer
local char = plr.Character
local grabbing = false
local grabbeditem = nil
local dist = 4
local userinputservice = game:GetService('UserInputService')
local mouse = plr:GetMouse()
local camera = workspace.CurrentCamera

mouse.Button1Down:Connect(function()
	if mouse.Target and mouse.Target.Locked then
		print("Block")
		grabbing = true
		grabbeditem = mouse.Target
		local bp = Instance.new("BodyPosition", grabbeditem)
	end
end)

mouse.Button1Up:Connect(function()
	grabbing = false
	if grabbeditem and grabbeditem:FindFirstChild("BodyPosition") then
		grabbeditem.BodyPosition:Destroy()
	end
end)

userinputservice.InputChanged:Connect(function(input, gameProcessedEvent)
	if not gameProcessedEvent then
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			if grabbing == true then
				grabbeditem.BodyPosition.Position = (CFrame.new(camera.CFrame.Position,camera.CFrame.LookVector)* CFrame.new(dist,0,0)).Position
			end	
		end
	end
end)

This should do it:

userinputservice.InputChanged:Connect(function(input, gameProcessedEvent)
	if not gameProcessedEvent then
		if input.UserInputType == Enum.UserInputType.MouseMovement then
			if grabbing == true then
				grabbeditem.BodyPosition.Position = ((camera.CFrame * CFrame.new(0,0,-dist)) ).Position
			end	
		end
	end
end)
2 Likes

Works like a charm, you’re a boss. Thank you so much!
Thank you @starmaq for the help too!

2 Likes