How to CFrame a part to a Ray without Ray.Position? (When Ray didn't hit anything)

I use this to CFrame my bullet trail part to the Ray I casted:

Bullet.CFrame = CFrame.new(MuzzlePosition + (RayPosition - MuzzlePosition)/2, RayPosition)

However, when the Ray doesn’t hit anything, I don’t have a RayPosition. So, is there a way I can get around this and still be able to have a bullet trail?

2 Likes

When you cast a ray, you had to specify the origin (Vector3) and directional vector (Vector3) with a particular length.

So if the ray doesn’t hit anything, you can add origin and direction in place of RayPosition.

MuzzlePosition + RayDirection

RayDirection was probably got this way:

local RayDirection = (mousePosition - MuzzlePosition).Unit * ARBITRARY_MAGNITUDE

Edit. @Nico_Nic77 thank you for noticing, I added the clarification.

3 Likes

I did this:

CachedBullet.CFrame = CFrame.new(MuzzlePosition + (MuzzlePosition + ViewRay.Direction), (MuzzlePosition + ViewRay.Direction))

But now the bullets go only in one direction on one of the axis once the ray goes out of range.

Also, I use :GetMouseLocation with ViewportPointToRay to get the RayDirection. Maybe it doesn’t work because of that.

local MouseLocation = UserInputService:GetMouseLocation()
local ViewRay = workspace.Camera:ViewportPointToRay(MouseLocation.X, MouseLocation.Y, 0)
local ray = workspace:Raycast(MuzzlePosition, ViewRay.Direction * 300)
1 Like

Update. Please see my latest reply.

Correctly visualised bullet trail:

Bullet.CFrame = CFrame.lookAt(
    origin + (origin + direction * LENGTH - origin)/2,
    origin + direction * LENGTH
)
-- or --
Bullet.CFrame = CFrame.lookAt(
     origin + (direction * LENGTH)/2, origin + direction * LENGTH
)
Old reply

I see, you’re close.

If I recall correctly, ViewRay is a unit vector (length of 1). That’s why you’re multiplying it by 300. If the ray doesn’t hit anything, we can use the point ray reaches starting at the origin and reaching toward the point in the given direction 300 studs away.

I haven’t got a chance to test this, but the following should get you the correct CFrame for the visualizer. It’s the same as if the ray does hit something, but replaces the intersection point with the point where the ray ends.

Bullet.CFrame = CFrame.lookAt(MuzzlePosition + (MuzzlePosition + ViewRay.Direction * 300 - MuzzlePosition)/2, MuzzlePosition + ViewRay.Direction * 300)
-- or
Bullet.CFrame = CFrame.lookAt(MuzzlePosition + (ViewRay.Direction * 300)/2, MuzzlePosition + ViewRay.Direction * 300)
2 Likes

Thank you! Both of the equations you gave me work.

1 Like

Hey there. After you gave me those equations I have worked on something else for some time. Now I have returned to making guns but realized that the bullet trail is actually off when you are not in first person.

Code:
I now cast two rays, first one from the mouse position in the 3D space (using ViewportPointToRay) and second one from the Muzzle to where the first ray hit. I also added the third number (0.15) when changing the size of the bullet, otherwise it was very very very thin, nearly undetectable.

local mouseRay = workspace:Raycast(ViewRay.Origin, ViewRay.Direction * 1000)
		if mouseRay then
		local gunRay = workspace:Raycast(MuzzlePosition, (mouseRay.Position - MuzzlePosition).Unit * 600)
			if gunRay ~= nil then
			local RayPosition = gunRay.Position
			CachedBullet.Size = Vector3.new((MuzzlePosition - RayPosition).Magnitude, 0.15, 0.15)
			CachedBullet.CFrame = CFrame.lookAt(MuzzlePosition + (RayPosition - MuzzlePosition)/2, RayPosition) * CFrame.Angles(0, math.rad(90), 0)
		else--If ray is out of range
				CachedBullet.CFrame = CFrame.lookAt(MuzzlePosition + (ViewRay.Direction * 600)/2, MuzzlePosition + ViewRay.Direction * 600) * CFrame.Angles(0, math.rad(90), 0)
				CachedBullet.Size = Vector3.new(600, 0.15, 0.15)
			end
		end

The outcome:
Ray3

Do you have some thoughts on why that is?

Oh yes, I see the issue. The equations are correct, but it’s not the view ray (camera ray) that is to be used. We need the direction we were casting towards from the muzzle. Missed that detail.

With the current code, if the ray is cast correctly and the gunRay doesn’t hit anything, the visualised bullet trail and an imaginary ray originating from our eyes directed towards the mouse should be parallel.

Great, I was just about to say that ScreenPointToRay() together with UIS:GetMouseLocation() can be the cause of an undesired offset because it depends on the gui inset.

Here is the code I quickly put together to see what properly works. It’s just a local script in a basic tool with a part called Handle which acts as the “Muzzle”.

Code
local UIS = game:GetService("UserInputService")

local gun = script.Parent
local muzzle = gun.Handle

local player = game:GetService("Players").LocalPlayer
local camera = workspace.CurrentCamera

gun.Activated:Connect(function()
	local mouseLocation = UIS:GetMouseLocation()
	local cameraRay = camera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
	
	local mouseRay = workspace:Raycast(cameraRay.Origin, cameraRay.Direction * 1000)
	
	if mouseRay then
		local gunRay = workspace:Raycast(
			muzzle.Position, (mouseRay.Position - muzzle.Position).Unit * 600
		)
		
		local trail = Instance.new("Part")
		trail.Anchored = true
		trail.CanCollide = false
		trail.BrickColor = BrickColor.new("Bright yellow")
		
		if gunRay ~= nil then
			trail.Size = Vector3.new((muzzle.Position - gunRay.Position).Magnitude, .15, .15)
			trail.CFrame = CFrame.lookAt(
				muzzle.Position + (gunRay.Position - muzzle.Position)/2, gunRay.Position
			) * CFrame.Angles(0, math.rad(90), 0)
		else
			trail.Size = Vector3.new(600, .15, .15)
			trail.CFrame = CFrame.lookAt(
				muzzle.Position + ((mouseRay.Position - muzzle.Position).Unit * 600)/2,
				muzzle.Position + (mouseRay.Position - muzzle.Position).Unit * 600
			) * CFrame.Angles(0, math.rad(90), 0)
		end
		trail.Parent = workspace
	end
end)
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.