By the way, the ray casts perfectly well when my camera is close to the character or in first person.
Script with casting ray and CFraming the bullet trail via PartCache module:
local AmmoFolder = workspace:WaitForChild("AmmoFolder")
local BulletTemplate = Instance.new("Part")
BulletTemplate.Anchored = true
BulletTemplate.CanCollide = false
BulletTemplate.Color = Color3.new(1, 0.835294, 0)
BulletTemplate.Transparency = 0.1
BulletTemplate.Shape = Enum.PartType.Cylinder
BulletTemplate.Material = Enum.Material.Neon
local BulletCache = ClientPartCach.new(BulletTemplate, 3, AmmoFolder)
function ShootBullet(ViewRay, Muzzle)
local MuzzlePosition = Muzzle.Position
if Ammo > 0 and Reloading == false then
local CachedBullet = BulletCache:GetPart()
local ray = workspace:Raycast(MuzzlePosition, ViewRay.Direction * 300)
if ray ~= nil then
local RayPosition = ray.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 the ray hit maximum range
CachedBullet.CFrame = CFrame.lookAt(MuzzlePosition + (ViewRay.Direction * 300)/2, MuzzlePosition + ViewRay.Direction * 300) * CFrame.Angles(0, math.rad(90), 0)
CachedBullet.Size = Vector3.new(300, 0.15, 0)
end
Ammo -= 1
AmmoGui.Text = Ammo
task.wait(0.1)
BulletCache:ReturnPart(CachedBullet)
elseif Ammo <= 0 and Reloading == false then
Reload(nil)
end
end
I get ViewRay like this:
local MouseLocation = UserInputService:GetMouseLocation()
local ViewRay = workspace.Camera:ViewportPointToRay(MouseLocation.X, MouseLocation.Y, 0)
ShootBullet(ViewRay, Muzzle)
local AmmoFolder = workspace:WaitForChild("AmmoFolder")
local BulletTemplate = Instance.new("Part")
BulletTemplate.Anchored = true
BulletTemplate.CanCollide = false
BulletTemplate.Color = Color3.new(1, 0.835294, 0)
BulletTemplate.Transparency = 0.1
BulletTemplate.Shape = Enum.PartType.Cylinder
BulletTemplate.Material = Enum.Material.Neon
local BulletCache = ClientPartCach.new(BulletTemplate, 3, AmmoFolder)
function ShootBullet(ViewRay, Muzzle, Spread)
local MuzzlePosition = Muzzle.Position
if Ammo > 0 and Reloading == false then
local CachedBullet = BulletCache:GetPart()
local unitRay = camera:ScreenPointToRay(mouse.X, mouse.Y, 1)
local ray = workspace:Raycast(MuzzlePosition, unitRay.Direction * 300)
if ray ~= nil then
local RayPosition = ray.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 the ray hit maximum range
CachedBullet.CFrame = CFrame.lookAt(MuzzlePosition + (ViewRay.Direction * 300)/2, MuzzlePosition + ViewRay.Direction * 300) * CFrame.Angles(0, math.rad(90), 0)
CachedBullet.Size = Vector3.new(300, 0.15, 0)
end
Ammo -= 1
AmmoGui.Text = Ammo
task.wait(0.1)
BulletCache:ReturnPart(CachedBullet)
elseif Ammo <= 0 and Reloading == false then
Reload(nil)
end
end
The issue is on what the ViewRay.Direction is. It works when the camera is close because the direction is for the camera. When you shoot it from the same direction but offset, it won’t land on the cursor. Replace the ViewRay.Direction with (MuzzlePosition - ViewRay.Position).Unit. This way, you now have the direction from the muzzle to the cursor point.
local ViewRay = workspace:Raycast(ViewRay.Origin, ViewRay.Direction * 100) -- You might not need the distance not 100% sure
-- Check if viewray succeeded
local newDirection = (MuzzlePosition - ViewRay.Position).Unit
ViewRay = workspace:Raycast(MuzzlePosition, newDirection* 300)
Here’s my raycast module, which accounts for that. Just call rayCast.rayCastToInput, and pass in the inputObject.
local RaycastModule = {}
-- Constants
local RAYCAST_DISTANCE = 1000
local Params = RaycastParams.new()
Params.FilterDescendantsInstances = {workspace.CurrentCamera}
local function rayCast(origin, direction,passed_Params)
local raycastParam = passed_Params or Params
local worldRay = Ray.new(origin, direction.Unit * RAYCAST_DISTANCE)
local raycastResult = workspace:Raycast(worldRay.Origin, worldRay.Direction * RAYCAST_DISTANCE, raycastParam)
-- Optional beam visualization
if raycastResult then
local size = Vector3.new(0.2, 0.2, (worldRay.Origin - raycastResult.Position).Magnitude-2)
local cframe = CFrame.lookAt(worldRay.Origin, raycastResult.Position) * CFrame.new(0, 0, -(size.Z/2)-2)
else
print("no hit")
end
return raycastResult
end
function RaycastModule.rayCastInDirection(origin, direction,passed_Params)
return rayCast(origin, direction,passed_Params)
end
function RaycastModule.rayCastToPoint(origin, goalPos,passed_Params)
local direction = goalPos - origin
return rayCast(origin, direction,passed_Params)
end
function RaycastModule.rayCastToInput(origin, inputObject,passed_Params)
local Camera = workspace.CurrentCamera
local worldRay = Camera:ScreenPointToRay(inputObject.Position.X, inputObject.Position.Y)
return rayCast(origin, worldRay.Direction,passed_Params)
end
return RaycastModule
I am confused on where to place these and whether I need to cast my original ray or the two rays that we cast from your code sketch is enough.
function ShootBullet(ViewRay, Muzzle, Spread)
local MuzzlePosition = Muzzle.Position
if Ammo > 0 and Reloading == false then
local CachedBullet = BulletCache:GetPart()
--Your code goes here?
local ViewRay = workspace:Raycast(ViewRay.Origin, ViewRay.Direction * 100) -- You might not need the distance not 100% sure
-- Check if viewray succeeded
local newDirection = (MuzzlePosition - ViewRay.Position).Unit
ViewRay = workspace:Raycast(MuzzlePosition, newDirection* 300)
local ray = workspace:Raycast(MuzzlePosition, ViewRay.Direction * 300)--Do I need this?
if ray ~= nil then
else--if the ray hit maximum range
end
--Some code blah blah
Here is the code that I have got with the use of your Module. I basically changed :ViewportToRay to :ScreenPointToRay and changed additional 2 line, it seems like this is how your module works but the result is that it does not work.
Did I miss something? Here is the script:
function ShootBullet(ViewRay, Muzzle, Spread)
local MuzzlePosition = Muzzle.Position
if Ammo > 0 and Reloading == false then
local CachedBullet = BulletCache:GetPart()
local worldRay = Ray.new(Muzzle.Position, ViewRay.Direction.Unit * 300)--I added this
local ray = workspace:Raycast(worldRay.Origin, worldRay.Direction * 300)--And this
--It seems like this is just like your module does it, but it still doesn't work. Did I miss something?
if ray ~= nil then
--Rest of the code
Here is how I get the ViewRay:
local MouseLocation = UserInputService:GetMouseLocation()
local ViewRay = workspace.Camera:ScreenPointToRay(MouseLocation.X, MouseLocation.Y)
ShootBullet(ViewRay, Muzzle)--Call the Shoot function
The rest of the code is identical to the one I have posted in the post.
Pretty sure the issue is that the ray is casted from the gun. If the ray was casted form the camera then it would work correctly
Now, you want the ray to start from the gun, so you have to calculate the direction vector yourself, you can’t use the one from the camera
To get this, you just have to substract Mouse.Hit with the guns position (the destination of the ray - the origin of the ray)
This does require 2 raycasts but I don’t think there is a way around it
I believe Mouse.Hit is using :GetMouse which is not recommended by Roblox, this is why I am using the recommended one, UserInputService:GetMouseLocation. Is there a way to do this with this method?
Ray.new() ins’t a raycast, it’s simply a data type to represent a ray
It has some nice methods, but that is basically it
Though it is a neat object to use when dealing with raycasts
Yeah, basically, you want to raycast a first ray to find Mouse.Hit, where the mouse intersects with the world, this can either be done using :GetMouse() or casting a ray yourself using ScreenPointToRay() or ViewportPointToRay (always forget which one to use). Then, using this value, you are able to find where the ray coming from the weapon would end up at. You can then raycast a second ray from the weapon to Mouse.Hit (where direction is Mouse.Hit.Position - Origin) and use that ray for your gun’s code
Imo, :GetMouse() is totally fine. The ease of use makes it really appealing as opposed to doing raycasting yourself. I did make a module that replicates Player:GetMouse() but uses the newer alternatives, if you are interested.