How to prevent projectiles from pooling up at muzzle spawn point

  1. What do you want to achieve? Keep it simple and clear!
    I want to have a gun that can maintain very high rates of fire of up to 1/600 a second seamlessly.

  2. What is the issue? Include screenshots / videos if possible!
    I have a gun that has a very high rate of fire and every time I try to fire my weapon at lets say 1/600 a second or even down to 1/50 a second there’s a lot of bullets that just pool up at the muzzle which causes some problems for me whether it be performance or they destroy each other for apparent reason despite being whitelisted to not destroy other bullets.

image

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I’m not sure how to solve this problem. I’m not sure if its an engine limitation or anything because im pretty sure even phantom forces are able to seamlessly instantiate bullets and have them fire at very high rates.

This is my code for instantiating the bullet

GunFired.OnServerEvent:Connect(function(player,ammo30,bullet30,Spawn30)
	if ammo30 > 0 then
		local bullet = bullet30:Clone()
		local bv = Instance.new("BodyVelocity")	
		bullet.CFrame = Spawn30.CFrame
		bullet.Parent = workspace
		bv.Parent = bullet
		bv.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
		bv.Velocity = Spawn30.CFrame.LookVector * 700
		ammo30 -= 1
		
		GunFired:FireAllClients(ammo30)
	end

end)

This is the bullet script that is responsible for self destruction and hit detection

local bullet = script.Parent
local now = tick()
--print("Bullet instantiated")
game:GetService("Debris"):AddItem(bullet,5)
bullet.Touched:Connect(function(hit)
	print("Hit something "..hit.Name)
	if hit.Parent:FindFirstChild("Humanoid") then
		print("Hit someone")
		local humanoid = hit.Parent.Humanoid
		humanoid.Health -= 10
		bullet:Destroy()
	else
		print("Hit something else "..hit.Name)
		if (hit.Name == "Bullet30" or hit.Name == "Bullet120") then
			--print("Hit something that wasnt a bullet")
			--bullet:Destroy()
			return
		end
		bullet:Destroy()
	end
end)

the thing is, i’m pretty sure phantom forces does not use body movers. they move it all with cframes. which means they don’t use roblox’s physics engine at all

what you can do is maybe have the effects run on clients only and have the server tell every client to do the same thing, or just do what phantom forces does and step away from roblox’s physics engine and handle everything yourself.

I’m not exactly sure why this is happening, but it could be due to the bullets having CanCollide true, it could also be a completely unexpected and random issue with Roblox, I’m honestly not too sure.

Also, just a heads up but you shouldn’t Parent any of your instances until you’ve set their properties and added their children, this could also be an issue.

Try this code:

local bullet30 = ... -- You shouldn't be passing instances through remotes
-- They take up a lot of bandwidth that way, just make a default bullet here and clone it.

GunFired.OnServerEvent:Connect(function(player,ammo30,Spawn30)
	if ammo30 > 0 then
    -- Likely don't need this task.spawn wrapper here
    -- but it could solve some issues, so we'll try it.
    task.spawn(function()
      local bullet = bullet30:Clone()
      local bv = Instance.new("BodyVelocity")	
      bullet.CFrame = Spawn30.CFrame
      bv.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
      bv.Velocity = Spawn30.CFrame.LookVector * 700
      bv.Parent = bullet
      bullet.Parent = workspace
    end)
		ammo30 -= 1

		GunFired:FireClient(Player, ammo30)
    -- No need to FireAllClients for one persons ammo.
	end
end)

And @MysteryOfHyper is correct, PF doesn’t use BodyMovers, in fact I believe they use a form of FastCast and raycast their bullets in increments to simulate their Muzzle Velocity.

1 Like

im thinking its the physics engine getting overwhelmed. whenever theres lots of simulated objects everything slows down and since they are firing 600+ bullets every second or more, roblox simply cant keep up.

1 Like

Jeez sounds complicated… Ill take a look into it since I do want that kind of high performance. Ill try out the code though, much appreciated!

Using task spawner made performance x100 worse and looks like making the bullet get instantiated by the server made it worse too

local ReplicatedStorage = game:WaitForChild("ReplicatedStorage")

local GunFired = ReplicatedStorage.TSFWeapons.ServerEvents.GunFired
local MoveArm = ReplicatedStorage.TSFWeapons.ServerEvents.MoveArm

local bullet = ReplicatedStorage.TSFWeapons.Bullet30:Clone()

GunFired.OnServerEvent:Connect(function(player,ammo30,Spawn30)
	GunFired.OnServerEvent:Connect(function(player,ammo30,Spawn30)
		if ammo30 > 0 then
			-- Likely don't need this task.spawn wrapper here
			-- but it could solve some issues, so we'll try it.
			--task.spawn(function()
				local bullet = bullet:Clone()
				local bv = Instance.new("BodyVelocity")	
				bullet.CFrame = Spawn30.CFrame
				bv.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
				bv.Velocity = Spawn30.CFrame.LookVector * 700
				bv.Parent = bullet
				bullet.Parent = workspace
			--end)
			ammo30 -= 1

			GunFired:FireClient(player, ammo30)
			-- No need to FireAllClients for one persons ammo.
		end
	end)
end)

Amended code but still having the bullet instantiated by the server made performance unacceptable

That’s quite an oddity, considering you were instantiating it on the server by cloning it in the first place…

Also, no need to clone Bullet30 when you assign bullet since you’re not going to be directly modifying it anyways.

And the task.spawn() is kind of expected, just was an experiment in all honesty.

Edit: Other than just using fastcast I’m genuinely not sure what you should do.

I forgot to got rid of the second clone and the performance is better however the bullet doesnt seem to work properly anymore. The offsets are strange and even direction is completely wrong.

and after using it for a while I get this error

The Parent property of Bullet30 is locked, current parent: NULL, new parent Workspace  -  Server - GunHandler:16
local bullet = ReplicatedStorage.TSFWeapons.Bullet30:Clone()

GunFired.OnServerEvent:Connect(function(player,ammo30,Spawn30)
	GunFired.OnServerEvent:Connect(function(player,ammo30,Spawn30)
		if ammo30 > 0 then
			-- Likely don't need this task.spawn wrapper here
			-- but it could solve some issues, so we'll try it.
			task.spawn(function()
				--local bullet = bullet:Clone()
			local bv = Instance.new("BodyVelocity")	
				bullet.Parent = workspace
				bullet.CFrame = Spawn30.CFrame
				bv.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
				bv.Velocity = Spawn30.CFrame.LookVector * 700
				bv.Parent = bullet
				
			end)
			ammo30 -= 1

			GunFired:FireClient(player, ammo30)
			-- No need to FireAllClients for one persons ammo.
		end
	end)
end)

I think Ill switch over to fastcast, I just tried out the debug gun, it was very impressive

No no, you do need to clone that, get rid of the task.spawn–

Nevermind I’ll just do it for you really quickly since that’s the easiest way to make it as I’m trying to explain.

oh my god.

local bullet = ReplicatedStorage.TSFWeapons.Bullet30
-- I was referring to the clone up here.

-- how the hell did you get another OnServerEvent wrapper????????
-- this is why the performance was going downhill fast
--GunFired.OnServerEvent:Connect(function(player,ammo30,Spawn30)
	GunFired.OnServerEvent:Connect(function(player,ammo30,Spawn30)
		if ammo30 > 0 then
			local _bullet = bullet:Clone()
			local bv = Instance.new("BodyVelocity")	
			_bullet.Parent = workspace
			_bullet.CFrame = Spawn30.CFrame
			bv.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
			bv.Velocity = Spawn30.CFrame.LookVector * 700
			bv.Parent = _bullet
			ammo30 -= 1

			GunFired:FireClient(player, ammo30)
			-- No need to FireAllClients for one persons ammo.
		end
	end)
--end)

Edit:
Elaborating on what you did wrong:
You wrapped GunFire.OnServerEvent:Connect() in another GunFire.OnServerEvent:Connect() call, which meant every single time you called it, you ended up making another connection that would ALSO be executed when you called FireServer from the client. So after a couple goes, you’re making like 20 bullets instead of 1.

Oh. Oops my bad, sorry its like 4am for me so I clearly didnt realise I was doing that ehehe…

Thanks for the clarification, will test immediately!

Yeah it works better, not sure if there was a difference between having the task spawner there or not but its more or less back to square 1 just simply more efficient with remote events I suppose… by the way is there any like “How to use Fast Cast” tutorial out there? I dont exactly learn the best way simply from documentation…

Fast cast looks like the only viable way forward at this point and I was always wondering if there was an easier way to achieve what I am doing. I always strive for efficiency in my code so everyone can enjoy sweet 60FPS.

Here’s the Community Resource page for FastCast, it has documentation.

2 Likes