Use that Ray (.Origin and .Direction) to raycast and find the cursor’s relative hit position via .Position; if there’s no intersection then just use the original Ray to form a really long vector as the end position
You could alternatively use Mouse.Hit and Mouse.Target to skip step 1 and 2, but that’s discouraged due to its limitations (.TargetFilter, etc)
Hitscan. Skip this step if the first raycast from step 2 failed to hit (the player is aiming the sky). Raycast again from the player’s gun to the cursor’s hit position. The .Position of this raycast is where the bullet will end up, and the .Instance is what it hit.
Figure out a way to draw a line between the player’s gun’s muzzle and that hitscan’s position (once again, use the cursor’s hit position from step 2 if the bullet didn’t hit anything). You could use Beams or do it manually with a narrow part positioned with :Lerp or something
Use the aforementioned .Instance from the second raycast to process hitreg, assuming that the bullet hit a player and not a wall
I did not explain replication or anti-exploit security, because that’s something for you to figure out on your own
So, ViewportPointToRay will take a position on your screen and then derive a 3D vector from it. Basically, it’s turning your 2-dimensional mouse position into a 3-dimensional world space position.
local uis = game:GetService'UserInputService'
local mPos: Vector2 = uis:GetMouseLocation() --get the player's cursor position
local ray: Ray = workspace.CurrentCamera:ViewportPointToRay(mPos.X, mPos.Y)
print(ray.Origin) --3D position of the cursor
print(ray.Direction) --direction of the cursor
And then, using returned ray, we can raycast to find the exact position that the player’s cursor hits:
local param: RaycastParams = RaycastParams.new()
param.FilterDescendantsInstances = {} --customize the parameters; include things such as the player's own gun
param.FilterType = Enum.RaycastFilterType.Blacklist
local rayVec: Vector3 = ray.Direction * 5000 --the 5000 is the length of the raycast below
local raycast: RaycastResult = workspace:Raycast(ray.Origin, rayVec, param)
--if the raycast fails (meaning that the player is aiming at the sky), approximate the hit position
local hitPosition: Vector3 = if raycast then raycast.Position else ray.Origin + rayVec
Now that we have the hit position of the player’s mouse, we will raycast again from the player’s gun to that hit position to see what the bullet hits:
local gun: PVInstance = randomgun.Muzzle --the player's gun's muzzle
local aimAt: Vector3 = hitPosition - gun.CFrame.Position --displacement between the gun and the hitPosition of the mouse; use it as the direction for the next raycast
local hitreg: RaycastResult = workspace:Raycast(gun.CFrame.Position, aimAt * 2, param) --param from before
--if the bullet hit something, target would be the basepart that it hit (such as a limb of another player)
local target: BasePart = if hitreg then hitreg.Instance else nil
--if the bullet didn't hit anything at all, use the previous hitPosition
local targetPosition: Vector3 = if hitreg then hitreg.Position else hitPosition
And then from there, making things such as bullet tracers and implementing player damage is self-explanatory
local player = game.Players.LocalPlayer
local UIS = game:GetService("UserInputService")
local mouse = player:GetMouse()
local ray = workspace.CurrentCamera:ViewportPointToRay(mouse.X, mouse.Y)
script.Parent.Activated:Connect(function()
print(ray.Origin)
print(ray.Direction)
end)
Also is this all in a local script or is it split inbetween both
ray should be placed inside the .Activated event since it’s a static value
Yes that portion of the code needs to be placed on the client; and on the server is where you will process all of the raycasts and hitregs and stuff, so just use an remote event to transfer over the ray information
local Tool = script.Parent
local player = game.Players.LocalPlayer
local UIS = game:GetService("UserInputService")
local Event = Tool.MainEvent
local mouse = player:GetMouse()
script.Parent.Activated:Connect(function()
local ray = workspace.CurrentCamera:ViewportPointToRay(mouse.X, mouse.Y)
Event:FireServer(ray)
end)
local Tool = script.Parent
local Event = Tool.MainEvent
local function Shoot(Player, ray)
print(Player)
print(ray)
print(ray.Origin)
print(ray.Direction)
end
Event.OnServerEvent:Connect(Shoot)
It definitely is, the player can trick the server into thinking that their camera is all the way across the map
However, it shouldn’t be problematic since the server will handle the important things like shooting position and hitreg which can’t be manipulated from the client
Alright, so it shouldn’t really be too big of a issue?, as in a exploiter cant just change the ray to be where ever they want (Ect, on another player behind multiple walls)
Anyways
Setting up the gun now
local Tool = script.Parent
local Event = Tool.MainEvent
local Params = RaycastParams.new()
Params.FilterDescendantsInstances = {}
Params.FilterType = Enum.RaycastFilterType.Blacklist
local function OnEquipped()
local C = {}
local Holder = Tool.Parent
table.insert(C, Holder)
for i, v in ipairs(workspace.Ignored:GetChildren()) do
table.insert(C, v)
end
Params.FilterDescendantsInstances = C
end
local function Shoot(Player, ray)
print(Player)
print(ray)
print(ray.Origin)
print(ray.Direction)
local Rv = ray.Direction * 300
local Raycast = workspace:Raycast(ray.Origin, Rv, Params)
end
Event.OnServerEvent:Connect(Shoot)
Tool.Equipped:Connect(OnEquipped)
And some more question because i just dont understand stuff easily
Is this a part where the bullet fires from? and if it is, am i able to use a attachment instead?
For this do i just use the bottom one, or is the top one important aswell?
Why is this multiplied by two?, i just dont understand the math here mainly
Yes, it is. That’s why I said that exploits shouldn’t be too problematic since the muzzle’s position is checked on the server
They’re both important. The top one is for the hitreg which is what the bullet hit, and the bottom one will be used for GFX such as placing bullet holes and drawing the bullet tracer
For safeguarding. If you didn’t multiply by two, the raycast will only search between the muzzle and the hit position, which isn’t always reliable since positions can shift due to latency and that would throw off the hitreg
I dont think we have used anything that we couldn’t do with mouse.hit, so basically to shorten some code couldn’t mouse.hit be used? or is there something im missing?
local Tool = script.Parent
local Event = Tool.MainEvent
local Params = RaycastParams.new()
Params.FilterDescendantsInstances = {}
Params.FilterType = Enum.RaycastFilterType.Blacklist
local function OnEquipped()
local C = {}
local Holder = Tool.Parent
table.insert(C, Holder)
for i, v in ipairs(workspace.Ignored:GetChildren()) do
table.insert(C, v)
end
Params.FilterDescendantsInstances = C
end
local function Shoot(Player, ray)
print(Player)
print(ray)
print(ray.Origin)
print(ray.Direction)
local Rv = ray.Direction * 300
local Raycast = workspace:Raycast(ray.Origin, Rv, Params)
local HitPos = if Raycast then Raycast.Position else ray.Origin + Rv
local ShootFrom = Tool.Handle.Attachment.WorldPosition
local AimAt = HitPos - ShootFrom
local HitDetection = workspace:Raycast(ShootFrom, AimAt * 2, Params)
local ThingHit = if HitDetection then HitDetection.Instance else nil
local PosHit = if HitDetection then HitDetection.Position else HitPos
if ThingHit then
print(ThingHit)
end
end
Event.OnServerEvent:Connect(Shoot)
Tool.Equipped:Connect(OnEquipped)
Also quick question, how come the hits a little off?
As in this
Its like im not clicking over the spawn, yet its still hitting it