Hi! I’ve come across a problem where I have Client-Sided Projectiles handling on all Clients, and when they hit a surface it fires a remote event on the Server that makes multiple hit registers where it should only happen once. This occurs because for every player (or client) loaded into the game, one person fires a projectile and it replicates that projectile to that person and all the other players in that server, which creates multiple projectiles on each client.
Sorry if I worded that out awkwardly so I will provide a video of it:
https://streamable.com/y7g2dj
As you can see when Player1 shoots a projectile it will replicate towards Player2’s client as I used a remote event from the Server to FireAllClients to let the clients know that Player1 fired a projectile. The issue is that when it hits the wall, the server thinks that 2 projectiles hit it and printed out x2 Hit texts in the output. This problem would be worse if there were 3 or more players in the server which in turn would have the server read multiple hit detections depending on how many players there are.
I’m using the FastCast Redux Module to handle the Projectile calculations on the client so that projectiles run smoothly:
local UserInputService = game:GetService("UserInputService")
local ServerScriptService = game:GetService("ServerScriptService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local Debris = game:GetService("Debris")
local Players = game:GetService("Players")
local Player = Players.LocalPlayer
local PlayerGui = Player.PlayerGui
local Character = Player.Character or Player.CharacterAdded:Wait()
local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart")
local FireButton = PlayerGui.ProjectileTest.TextButton
local ProjectilesFolder = workspace:FindFirstChild("ProjectilesFolder")
local ProjectileStorage = ReplicatedStorage:FindFirstChild("ProjectileStorage")
local ClientProjectiles = ReplicatedStorage:FindFirstChild("ClientProjectiles")
local FireProjectile = ReplicatedStorage:FindFirstChild("FireProjectile")
local ProjectileHit = ReplicatedStorage:FindFirstChild("ProjectileHit")
local FastCast = require(ReplicatedStorage:FindFirstChild("FastCastRedux"))
local castParams = RaycastParams.new()
castParams.FilterType = Enum.RaycastFilterType.Exclude
castParams.IgnoreWater = true
FastCast.VisualizeCasts = false
UserInputService.InputBegan:Connect(function (input, isTyping)
if isTyping then return end
if input.UserInputType == Enum.UserInputType.MouseButton1 then
FireProjectile:FireServer(Character, HumanoidRootPart)
end
end)
FireButton.MouseButton1Down:Connect(function()
FireProjectile:FireServer(Character, HumanoidRootPart)
end)
local function onLengthChanged(cast, lastPoint, direction, length, velocity, projectile)
if projectile then
local projectileLength = projectile.Size.Z/2
local offset = CFrame.new(0, 0, -(length - projectileLength))
projectile.CFrame = CFrame.lookAt(lastPoint, lastPoint + direction):ToWorldSpace(offset)
projectile.CFrame = projectile.CFrame:ToWorldSpace(CFrame.Angles(math.rad(-90), 0, 0))
end
end
local function CastTerminating(cast)
local projectile = cast.RayInfo.CosmeticBulletObject
Debris:AddItem(projectile, 0.01)
end
local function onRayHit(cast, result, velocity, projectile)
local hit = result.Instance
local hitPos = projectile.CFrame
ProjectileHit:FireServer(hitPos, hit)
end
ClientProjectiles.OnClientEvent:Connect(function(Character, HumanoidRootPart)
RunService.Stepped:Wait()
--print(Index)
local Missile = ProjectileStorage.Missile:Clone()
local castBehavior = FastCast.newBehavior()
castBehavior.RaycastParams = castParams
castBehavior.AutoIgnoreContainer = false
castBehavior.CosmeticBulletContainer = ProjectilesFolder
castBehavior.CosmeticBulletTemplate = Missile
castBehavior.MaxDistance = 250
castParams.FilterDescendantsInstances = {Character, ProjectilesFolder}
local caster = FastCast.new()
local origin = HumanoidRootPart.Position
local direction = (HumanoidRootPart.CFrame.LookVector).Unit
local missileCast = caster:Fire(origin, direction, 30, castBehavior)
missileCast.RayInfo.CosmeticBulletObject.CFrame = missileCast.RayInfo.CosmeticBulletObject.CFrame:ToWorldSpace(CFrame.Angles(math.rad(-90), 0, 0))
caster.CastTerminating:Connect(CastTerminating)
caster.LengthChanged:Connect(onLengthChanged)
caster.RayHit:Connect(onRayHit)
end)
Then I have the FireAllClients function and the Server-Sided Hit detection handled on the server:
local HttpService = game:GetService("HttpService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Debris = game:GetService("Debris")
local FireProjectile = ReplicatedStorage:WaitForChild("FireProjectile")
local ProjectileHit = ReplicatedStorage:WaitForChild("ProjectileHit")
local ClientProjectiles = ReplicatedStorage:WaitForChild("ClientProjectiles")
FireProjectile.OnServerEvent:Connect(function(plr, Character, HumanoidRootPart)
ClientProjectiles:FireAllClients(Character, HumanoidRootPart)
end)
ProjectileHit.OnServerEvent:Connect(function(plr, HitPos, TargetHit)
print("Hit")
local Explosion = Instance.new("Explosion")
Explosion.BlastPressure = 0
Explosion.DestroyJointRadiusPercent = 0
Explosion.Parent = workspace
Explosion.Position = HitPos.Position
Debris:AddItem(Explosion, 1)
end)
Is there a way where I can call a specific Client-Sided projectile to the Server so that it can only Hit once? All help is appreciated thanks!