Bullet Origin accuracy when it comes to client sided viewmodels (FastCast)

I’ve been trying to utilize view models for my game, but the position of the attachment where the bullet should come from is different from the position of the view model’s weapon.

How would I make it more accurate without it looking weird for the client and server side of the players?

It seems that your bullet script is considering the origin from the servers perspective. Can you show the script?

--Events and parts of the gan
local MouseEvent = script.Parent:WaitForChild("MouseEvent")
local tool = script.Parent
local FastCast = require(tool.FastCastRedux)
local caster = FastCast.new()
local bulletsFolder = game.Workspace:WaitForChild("bulletsFolder")
local debris = game:GetService("Debris")
local KeyEvent = script.Parent:WaitForChild("KeyEvent")

--sound management
local Sounds = script.Parent:WaitForChild("Handle")

local gunshotsfx = Sounds:WaitForChild("M1911 Gun Sound")
gunshotsfx.RollOffMode = Enum.RollOffMode.Linear
gunshotsfx.RollOffMinDistance = 30
gunshotsfx.RollOffMaxDistance = 100
gunshotsfx.Volume = 0.5

local reloadsfx = Sounds:WaitForChild("Pistol Reload Sound")
reloadsfx.EmitterSize = 3
reloadsfx.RollOffMode = Enum.RollOffMode.Linear
reloadsfx.RollOffMinDistance = 10
reloadsfx.RollOffMaxDistance = 20
reloadsfx.Volume = 0.5
--These are the stats of the gun
local stats = tool.Stats
local damage = stats.Damage.Value
local bulletspeed = stats.Velocity.Value
local headshotMultiplier = stats.Headshot_Multiplier.Value
local reserve = stats.Reserve
local mag = stats.Mag
local magSize = stats.Mag_size.Value
local rTime = stats.Reload_time.Value
local isReloading = stats.Is_Reloading.Value
local Pierce = stats.Pierce.Value

FastCast.VisualizeCasts = false

--Cosmetic Bullets
local bulletTemplate = Instance.new("Part")
bulletTemplate.Anchored = true
bulletTemplate.Parent = bulletsFolder
bulletTemplate.CanCollide = false
bulletTemplate.Size = Vector3.new(0.1,0.1,2)
bulletTemplate.Material = Enum.Material.Neon
bulletTemplate.TopSurface = Enum.SurfaceType.Smooth
bulletTemplate.CanQuery = false

local castParams = RaycastParams.new()
castParams.FilterType = Enum.RaycastFilterType.Exclude
castParams.IgnoreWater = true
castParams.RespectCanCollide = true

local castBehavior = FastCast.newBehavior()
castBehavior.RaycastParams = castParams
castBehavior.AutoIgnoreContainer = false
castBehavior.CosmeticBulletContainer = bulletsFolder
castBehavior.CosmeticBulletTemplate = bulletTemplate

tool.Equipped:Connect(function()
	castParams.FilterDescendantsInstances = {tool.Parent}	
	local firepoint = tool.Handle.FirePoint
	firepoint.WorldCFrame = tool.Bolt.CFrame
	castBehavior.CanPierceFunction = CanRayPierce
end)

function OnLengthChanged(cast, lastpoint, direction, length, velocity, bullet)
	if bullet then
		local bulletLength = bullet.Size.Z/2
		local offset = CFrame.new(0,0, -(length - bulletLength))
		bullet.CFrame = CFrame.lookAt(lastpoint, lastpoint+direction):ToWorldSpace(offset)
	end
end

function reload()
	if mag.Value ~= magSize and isReloading == false then
		isReloading = true
		reloadsfx:Play()
		wait(rTime)
		if reserve.Value > 0 and isReloading == true then
			local sub_ammo = magSize - mag.Value
			if reserve.Value - sub_ammo > 0 then
				mag.Value += sub_ammo
				reserve.Value -= sub_ammo
			else
				mag.Value += reserve.Value
				reserve.Value = 0
			end

			isReloading = false
		end
	end
end

function shoot(player, mousehit)
	local origin = tool.Handle.FirePoint.WorldPosition
	local direction = (mousehit - origin).unit
	if mag.Value > 0 and isReloading == false then
		gunshotsfx:Play()
		caster:Fire(origin, direction, bulletspeed, castBehavior)
		mag.Value -= 1
	else
		if isReloading == false then
			reload() 
		end
	end
end



function CanRayPierce (cast, result, segmentVelocity)
	local hits = cast.UserData.Hits

	if (hits == nil) then
		cast.UserData.Hits = 1
	else
		cast.UserData.Hits += 1
	end

	if (cast.UserData.Hits == Pierce) then
		return false
	end



	local hit = result.Instance
	castBehavior.CanPierceFunction = CanRayPierce
	local character = hit:FindFirstAncestorWhichIsA("Model")
	if character and character:FindFirstChild("Humanoid") then
		if character.Humanoid.Health > 0 then
			if hit.Name == "Head" or hit.Name == "head" then
				character.Humanoid:TakeDamage(damage * headshotMultiplier/cast.UserData.Hits)
			else
				character.Humanoid:TakeDamage(damage/cast.UserData.Hits)
			end
		end
	end

	return true
end

function onRayHit(cast, result, velocity, bullet)
	local hit = result.Instance

	local character = hit:FindFirstAncestorWhichIsA("Model")
	if character and character:FindFirstChild("Humanoid") then
		if character.Humanoid.Health > 0 then
			if hit.Name == "Head" or hit.Name == "head" then
				character.Humanoid:TakeDamage(damage * headshotMultiplier)
			else
				character.Humanoid:TakeDamage(damage)
			end
		end
	end
	

end


MouseEvent.OnServerEvent:Connect(shoot)

caster.RayHit:Connect(onRayHit)

KeyEvent.OnServerEvent:Connect(reload)

caster.LengthChanged:Connect(OnLengthChanged)

Sorry if its a bit messy, I’ve been trying to figure out fast cast while making this. I’ve been thinking of creating a “fake cast” on the client, but I think that would be a bit too resource intensive, not to mention that the server side’s can look different ;-;

Not really, fast cast is already pretty performant and the method you mentioned is the one used widely. If you create bullets on the server then it could cause massive delays for players with just above 100 ping.

And, you should send the origin on the bullet to the server via the remote event you are using if you don’t want to use the “fake cast” system. This does mean that exploiters could potentially break the system but you can just add checks on the server. For example, seeing how far the origin of the tool (on the server) is from that the client sent.

-- client sends us this: origin = tool.Handle.FirePoint.WorldPosition
function shoot(player, origin, mousehit)
	local direction = (mousehit - origin).unit
	if mag.Value > 0 and isReloading == false then
		gunshotsfx:Play()
		caster:Fire(origin, direction, bulletspeed, castBehavior)
		mag.Value -= 1
	else
		if isReloading == false then
			reload() 
		end
	end
end

If fast cast is very performant, I’ll be going for the “fake cast”. Thanks for the insights :slight_smile:

How can I make the server bullet invisible for the client without it affecting any of the server bullets from other players? I know localtransparency modifier exists, so I tried using Child added, but it also affects bullets that aren’t from the client. Does the fast cast api have anything I can use for that?

I don’t know much about fast cast but one thing you could do is to just destroy the bullets on the server side.

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