im currently using a projectile system that spawns projectiles on the server and then uses applyimpulse on them on the client however the problem that im facing is that the server appears to spawn things like explosions on impact in the position that the projectile was in a bit before it hit something, making it impossible to damage an enemy
heres an example:
i currently cannot use something like fastcast or raycasting, as some of my projectiles don’t move as quickly as the rocket in the video above would (these slower projectiles register hits perfectly fine btw):
How are you detecting collisions? is it a .Touched event? I think the projectile is hitting your own character so you could add some code to have it ignore your own character.
it prints the name of the item that it the player can see it touch
server code:
Remotes.SpawnProjectile.OnServerInvoke = function(Player, Weapon, SpawnCFrame)
local WeaponData = require(Components.WeaponData[Weapon.Name])
local ProjectileID = GenerateSafeProjectileID(#workspace.Projectiles:GetChildren())
local Projectile = Components.Projectiles[Weapon.Name]:Clone()
Projectile.CFrame = SpawnCFrame
Projectile.CollisionGroup = Enums.CollisionGroup.Projectiles
Projectile.Parent = workspace.Projectiles
Projectile:SetAttribute("ProjectileID", ProjectileID)
Projectile:SetNetworkOwner(Player)
local ShootSound = Components.SoundEffects.Weapons[Weapon.Name].Shoot:Clone()
ShootSound:Play()
ShootSound.Parent = Player.Character.HumanoidRootPart
coroutine.wrap(function()
ShootSound.Stopped:Wait()
ShootSound:Destroy()
end)()
local Hitbox = Instance.new("Part")
Hitbox.Name = "Hitbox"
Hitbox.Size = WeaponData.DamageHitboxSize
Hitbox.Transparency = 0.8
Hitbox.Material = Enum.Material.Neon
Hitbox.CanCollide = false
Hitbox.CanQuery = false
Hitbox.CanTouch = true
Hitbox.Massless = true
Hitbox.Color = Color3.fromRGB(0, 0, 255)
local Weld = Instance.new("Weld")
Weld.Part0 = Hitbox
Weld.Part1 = Projectile
Weld.Parent = Projectile
Hitbox.Parent = Projectile
Hitbox:SetNetworkOwner(Player)
local HitSound = Components.SoundEffects.Weapons[Weapon.Name]:FindFirstChild("Hit")
local HitSoundTimesPlayed = 0
local AlreadyHitEnemies = {}
Projectile.Touched:Connect(function(Hit)
if Hit:FindFirstAncestorOfClass("Model") ~= Player.Character then
if HitSound then
if HitSoundTimesPlayed < tonumber(HitSound:GetAttribute("PlayLimit")) then
HitSoundTimesPlayed += 1
PlayHitSound(HitSound, Projectile)
end
end
end
end)
local AlreadyCalledOnProjectileTouched = false
Hitbox.Touched:Connect(function(Hit)
local Character = Hit:FindFirstAncestorOfClass("Model")
if Character then
if not table.find(AlreadyHitEnemies, Character) and Character:FindFirstChild("Humanoid") then
if #AlreadyHitEnemies < WeaponData.MaximumUniqueEnemiesDamaged and Character ~= Player.Character then
table.insert(AlreadyHitEnemies, Character)
ServerUtils:DamageEnemyHumanoid(Character, WeaponData.Damage)
if Character.Humanoid.Health <= 0 then
Remotes.PlayHitmarker:FireClient(Player, Enums.Hitmarker.Kill)
else
Remotes.PlayHitmarker:FireClient(Player, Enums.Hitmarker.Normal)
end
if HitSound then
if HitSoundTimesPlayed < tonumber(HitSound:GetAttribute("PlayLimit")) then
HitSoundTimesPlayed += 1
PlayHitSound(HitSound, Projectile)
end
end
end
end
end
if WeaponData.OnProjectileTouched ~= nil and AlreadyCalledOnProjectileTouched == false then
if Character then
if Character == Player.Character then
return
end
end
AlreadyCalledOnProjectileTouched = true
local PlayerHit = WeaponData.OnProjectileTouched(Player, Hit, Projectile)
if not table.find(AlreadyHitEnemies, PlayerHit) then
table.insert(AlreadyHitEnemies, PlayerHit)
end
end
end)
Weapon:Destroy()
coroutine.wrap(function()
task.wait(20)
if Projectile ~= nil then
Projectile:Destroy()
end
end)()
return ProjectileID
end
client code:
function Weapon:LaunchProjectileInDirection(Direction)
if Player:GetAttribute("TitlescreenEnabled") == false then
self._LoadedAnimations.Throw:Play()
local Viewmodel = workspace.CurrentCamera:FindFirstChild("Viewmodel")
local SpawnPosition = nil
if Player:GetAttribute("ThirdPersonEnabled") == true then
SpawnPosition = Player.Character.HumanoidRootPart.CFrame
else
SpawnPosition = Viewmodel[self.Tool.Name].Handle.CFrame
end
local ProjectileID = Remotes.SpawnProjectile:InvokeServer(self.Tool, SpawnPosition)
for _, Projectile in pairs(workspace.Projectiles:GetChildren()) do
if Projectile:GetAttribute("ProjectileID") == ProjectileID then
Projectile:ApplyImpulse(Direction * self.Data.ProjectileVelocity)
end
end
end
self:Destroy()
end
edit: i forgot to add it but the onprojectiletouched function just creates an explosion
I mean try and figure out what part it is falsely hitting so you can see whats causing the issue! Anyways here’s my guess:
It might be hitting the tool the player has equipped. The Tool class is a subclass of the Model class so even if it’s under your player it’s first-model-ancestor would return the tool and bypass this check.
Try if not Hit:isDescendantOf(Player.Character) then
i dont believe it is a problem with the .touched function but rather the fact that it seems to be a syncing issue between the client and the server and im not sure how to fix it
i guarantee its 100% not it hitting the players character - sometimes it even happens 40-50 studs after the player shot their weapon
Ah I see, whenever it’s physics replication shenanigans I just assume it’s NetworkOwner problems.
I noticed the code is doing this in the following order. Projectile:SetNetworkOwner(Player) Welding Projectile & Hitbox together Hitbox:SetNetworkOwner(Player)
Maybe you should weld, then set both network owners afterwards? You could also use GetNetworkOwner to check it’s actually applying, sometimes roblox automatically changes the network owner when you change certain things in an assembly.
i fixed this problem by using 2 different methods of handling projectiles:
for slower projectiles that bounce around and dont get destroyed on impact, i use the create projectile on server and applyimpulse on client methods
for fast projectiles that are destroyed on impact, i use fastcast. cosmetic bullets are rendered on the client however hits are registered on the server - this completely eliminates the problem that i had