I’m working on an fps, the model will work as so:
- Client presses the right mouse button
- Client receives instant feedback of shot, recoil is shown, sound is made and local shot is fired
- A simple check is done on the Client to check if the shot hits to show the correct hit markers
- The Client tells the Server about the shot by including the origin of the shot and the time fired
- The Server checks if the shot is valid by grabbing where the players were at the time of the shot, and using the old positions to calculate if the shot hits the player or not (to account for lag)
- The Server damages the player it hits, and other Clients are also informed of the shot being fired
This is the current script I’m using (In the server):
local inducedPing = 0.5 --500ms (for testing purposes only)
local positions = {
["StarterCharacter"] = {
}
}
local sChr = workspace.StarterCharacter
local pos1 = CFrame.new(-30, 5.8, -30)
local pos2 = CFrame.new(30, 5.8, -30)
local lr = 0
local t = 200
local state = true
local fp = 0
game:GetService("RunService").Stepped:Connect(function()
if state == true then
lr = lr + 1
if lr == t then
state = false
end
else
lr = lr - 1
if lr == 0 then
state = true
end
end
sChr:SetPrimaryPartCFrame(pos1:lerp(pos2, lr/t))
fp = fp + 1
if fp%3 == 0 then
table.insert(positions.StarterCharacter, #positions.StarterCharacter+1, {})
for i,v in pairs(sChr:GetChildren()) do
if v:IsA("BasePart") then
positions.StarterCharacter[#positions.StarterCharacter][v.Name] = v.CFrame
end
end
local len = #positions.StarterCharacter
if len == 20 then
table.remove(positions.StarterCharacter, 1)
end
end
end)
local fakechr = game.ReplicatedStorage.FakeCharacter
game.ReplicatedStorage.shoot.OnServerEvent:Connect(function(player, hitplayer, tic, origin, tar) --tar is just temp to calculate ray
local ping = inducedPing + (tick() - tic)
wait(ping)
local pr = #positions.StarterCharacter - math.min(math.ceil(ping*20 - 0.5), 19)
local chr = fakechr:Clone()
for i,v in pairs(chr:GetChildren()) do
v.CFrame = positions[hitplayer.Name][math.floor(pr)][v.Name]:lerp(positions[hitplayer.Name][math.ceil(pr)][v.Name], pr%1)
end
chr.Parent = workspace
local ray = Ray.new(origin.p, (tar - origin.p).unit * 500)
local hit, position = workspace:FindPartOnRayWithWhitelist(ray, {chr})
if hit then
print("hit")
wait(0.5)
end
chr:Destroy()
end)
What it does is it stores the CFrames of the Characters every 1/20th of a second, then using ping it calculates the mid point between the CFrames. For example if the ping results in a value of 11.66
it will grab a value that is 66.6% between 11 and 12 CFrame as an estimate will most likely be accurate instead of storing it every frame.
Unfortunately, if this was a server of 30 players it could be storing up to 6,600 CFrames at any given time (because players * framesstored * numberofbodyparts, 30 * 20 * 11 = 6,600.
This could be quite intensive however, and thats what I am worried about, I will have to recreate all of the CFrames of all the players when a shot is fired just to check where it hits using fake models.
Another problem I’m worried about is backtracking where they could pretend they have a higher ping then they actually do and hit where the player was and not where they were.
I’m not really sure a good method to getting rid of the lag the player may experience, however, this does seem to be accurate atleast 99% of hits being detected even at really high pings.