How would i go about making a gun system like this?

Now i didn’t specify what gun system i wanted my guns to be like in the title, as if i did not as many people would click on it.

Basically I’m looking to (try) to make a gun system similar to r2da’s system aka like this


(Bullets and the moving arms are client sided, although i want a option where people can see everyones bullets)

Also it was kind of done in here, looks cool

3 Likes
  1. Use camera:ViewportPointToRay to get the player’s cursor’s relative 3D LookVector

  2. 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)

  1. 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.

  2. 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

  3. 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 :wink:

2 Likes

ok, ill try to decipher your funny words :+1:

1 Like

Is there any tutorials or explainations what this is and how to use it?, ive never seen this ever

1 Like

There’s a documentation URL linked to that, just click on it

1 Like

Yeah, ive looked at it although i still dont really understand what it does

1 Like

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

1 Like

ok so something like

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

1 Like

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

1 Like

When firing the remote event do i send over the entire ray? or just the ray.Origin, and the ray.Direction?

1 Like

i dont think it matters unless there’s some replication issue with the Ray itself

1 Like

Alright heres what i set up

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)

prints everything fine

Also small question, is the ray exploitable?

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

Also, I’ve made an entire demo just for you:
gunToolTest.rbxl (44.8 KB)


Note that everything is just for demonstration purposes; there isn’t any sanity checks/anticheat implemented

1 Like

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

oo ill check it out for sure, :+1:

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

Alright, i see now.

Also just a question

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?

It’s mainly because of this:


I’m encouraging you to use UserInputService over mouse to get you into the habit of doing so in the future :slightly_smiling_face:

Damn didnt even know, alright

Alright so for the code

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