How can i approach handling projectiles?

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):

1 Like

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.

yea im using .touched and nah thats not the problem ive already done a check to make sure that it isn’t the player’s character

Have you tried printing the name of what it’s hitting to narrow it down? Also showing code would help otherwise I’m just going off of a hunch here.

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.

ive just tried both methods but it still appears to not work

i fixed this problem by using 2 different methods of handling projectiles:

  1. for slower projectiles that bounce around and dont get destroyed on impact, i use the create projectile on server and applyimpulse on client methods

  2. 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

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