The problem: My projectiles, when fired, behave weirdly due to lag. This ranges from projectiles stuttering in mid-air, to having weird collision detection
The solution: Some clever use of the client and BasePart:SetNetworkOwner()
DISCLAIMER: This tutorial is for solving a common issue with physics-based projectiles, like ones from the classic ROBLOX Slingshot or Paintball Gun. For guns that use raycasting for hit detection, you may want to look into alternative solutions.
First, we need a functioning ranged weapon. This is simple, with some light math involved in its creation. We need to create a handleless Tool, then insert a Script, a LocalScript, and a RemoteFunction with the name "MouseLocation" inside of it. Then, a RemoteEvent should be placed in ReplicatedStorage.
Script:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local POSITION_OFFSET = 8
local PROJECTILE_SPEED = 85
local SERVER_HITBOX_TRANSPARENCY = 1
local tool = script.Parent
local player = tool.Parent.Parent
local remote = tool.MouseLocation
local event = ReplicatedStorage.RemoteEvent
local head = player.Character.Head
tool.Activated:Connect(function()
local mouseposition = remote:InvokeClient(player) --gets the mouse from localscript
local spawnpos = head.Position + ((mouseposition - head.Position).unit * POSITION_OFFSET)
local velocity = (mouseposition - head.Position).unit * PROJECTILE_SPEED
local projectile = Instance.new("Part")
projectile.Size = Vector3.new(1, 1, 1)
projectile.Shape = Enum.PartType.Ball
projectile.Position = spawnpos
projectile.Velocity = velocity
projectile.Transparency = SERVER_HITBOX_TRANSPARENCY
projectile.Parent = workspace
projectile:SetNetworkOwner(nil) --this is the invisible server hitbox projectile
projectile.Touched:Connect(function() --when server hitbox hits something, it destroys itself and explodes
local explosion = Instance.new("Explosion")
explosion.Position = projectile.Position
explosion.Parent = workspace
projectile:Destroy()
end)
event:FireAllClients(spawnpos, velocity, projectile) --tell the localscript to create a visual copy
end)
LocalScript:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local player = Players.LocalPlayer
local mouse = player:GetMouse()
local tool = script.Parent
local remotefunc = tool:WaitForChild("MouseLocation")
local event = ReplicatedStorage:WaitForChild("RemoteEvent")
--client doesnt receive these objects instantly unlike server
remotefunc.OnClientInvoke = function()
return mouse.Hit.p --returns position of mouse to the server
end
event.OnClientEvent:Connect(function(spawnpos, velocity, projectile)
local visualcopy = projectile:Clone()
visualcopy.Position = spawnpos
visualcopy.Velocity = velocity
visualcopy.Transparency = 0
visualcopy.Parent = workspace --visual copy of server projectile
projectile.AncestryChanged:Connect(function()
visualcopy:Destroy() --when server projectile is destroyed, visual copy destroys itself also
end)
end)
If set up right, the tool should behave normally with no errors. Remember, the tool needs to be handleless!
Since we've created a visual copy of the projectile on every connected player's client, the projectile should look smooth for everyone. Furthermore, since we set the network owner of the server projectile to "nil", it means that the server has full network ownership over the server's projectile. That means that, instead of the projectile shifting between networks like with automatic network ownership, the projectile instead always sticks with the server's network.
If we didn't set the network ownership of the server projectile, the projectile would be heavily desynchronized, often looking like its exploding sooner than it should, changing the position of the explosion to somewhere far behind the impact point. If we didn't create the visual copies however, the projectile would look very jittery for everyone. Doing this allows you to have accurate physics-based projectiles, but also allow them to look good. For competitive games where precise aim is key, this information is invaluable for any ROBLOX developer.
I hope you've been able to glean something from this article, and hope you have a wonderful rest of your day.