Performant way to shapecast?

I currently have a functioning projectile system designed to handle hundreds to thousands of projectiles at a time, the issue comes when I try to add hitboxes to them. I’ve tried various open source hitbox modules, but they either A are too unreliable for this case of handling so many projectiles, or B use raycasts which don’t fit my needs, as my game will be using projectiles of varying sizes from small to massive.

So the solution was to use shapecasts, and they work well but I do get a bit of lag whenever I spawn a projectile using shapecasts.

Here’s the code for reference:

	function ProjectileHandler:Activate()
		local function updateProjectiles()
			
			local CFrameList:{CFrame} = {}
			local PartList:{Part} = {}

			for _,Projectile in Projectiles do
				table.insert(PartList,Projectile.Instance)			

				local DeltaT = (workspace:GetServerTimeNow()  - Projectile.LastTime)
				local LT = workspace:GetServerTimeNow() - Projectile.StartTime
				if LT >= Projectile.Lifetime then
					ProjectileHandler:DestroyProjectile(Projectile)
				end								
				local S = Projectile.Speed
				local CalculatedCFrame = CFrame.lookAt(Projectile.StartPosition + (Projectile.Velocty * S * DeltaT),Projectile.Instance.Position + Projectile.Velocty)
				Projectile.StartPosition = CalculatedCFrame.Position
				Projectile.LastTime = workspace:GetServerTimeNow()
				
				table.insert(CFrameList,CalculatedCFrame)
				
				local Magnitude = (Projectile.Instance.Position - CalculatedCFrame.Position).Magnitude
				Magnitude = math.floor(math.clamp(Magnitude,1,1023))
				local Cast = workspace:Spherecast(Projectile.Instance.Position,Projectile.Radius,CFrame.lookAt(Projectile.Instance.Position,CalculatedCFrame.Position).LookVector * Magnitude,ShapeCastParams)
				if Cast then
					print(Cast)
				end
			end

			workspace:BulkMoveTo(PartList,CFrameList)
		end


		RunService.PostSimulation:Connect(updateProjectiles)
	end

You could use multiple ray casts in the shape you want to make, although to my knowledge that would be quite difficult. There is also the option of using a hitbox updated to the projectiles position every frame. Give me a few minutes and I’ll try and work something out for you.

You shouldn’t see lag spikes like that with just a few projectiles. My guess is that the lag is coming from the output, which happens if you have multiple prints in a short period of time (especially of objects such as raycast results), which you are doing.

You may run into issues if you have hundreds to thousands of projectiles like you claim since all projectiles are under the same RunService connection, so you may want to batch the spherecast so that only a few projectiles raycast in each frame, though of course this can start adding some missed collisions.

Hey i didn’t even think about that smarty pants.

Well I thought the same thing about the print statements, but the lag still occurs even if I remove the print:

For reference, this is what it looks like without spherecasting at all:

To add to what @MayorGnarwhal said, do you have slower moving projectiles that wouldn’t need to be updated as often?
How big are your shapecasts? I’d imagine if they were larger they may take a bit more time to process.

As to your last post, maybe try taking out small sections of code that could cause the lag, like recognizing the hits of the shapecast (make it, just don’t process the hits) to see if that’s the section of code that is lagging.

Running thousands of these is gonna lag no matter what you are doing.
Maybe try the Touched event (I know, possible issues with lag, but it might be less) and figure out how fast each projectile is moving to calculate it’s hitbox using a transparent Part that’s the length based on the farthest distance the projectile can travel at that speed to keep from ‘shooting through’ items. For example if a projectile can travel 6 studs per frame make a hitbox that’s 7 studs (slightly longer) long. If the projectile might travel 50 studs per frame you’d need a hitbox that’s at least 50 studs long.

1 Like

The only line causing the lag is the actual line where I’m doing the workspace:Spherecast() call. When I remove that it works without lag.

And my shape casts are as big as the actual size of the projectile. My system is designed to make projectiles of any size, so I need a hitbox system that accommodates projectiles of various sizes. In the videos I sent the radius of those projectiles is 3. The length of the shapecasts is as big as they can be, since the limit for shapecast lengths is 1024 studs at the moment.

The thing that’s concerning me right now is that even when I fire a single projectile it lags.

And using the temporary parts with the touched event seems like an even worse idea. Shapecasting is causing lag, but instancing and destroying parts every frame isn’t going to be much more performant.

Ah wait, found the issue. I’m using objectcache for getting new parts for the projectiles, and objectcache puts parts like millions of studs out, and the shapecast was trying to cast using that several million stud magnitude in that first frame when the part is being retrieved. Just had to make some minor changes and it’s good now, not a shapecast issue but a vector issue.

Works great now

Nice to see you got it working, but I have a question. Why use the size limit of the shapecast?
Why not move a shapecast the size of the projectile with the projectile?

In your video the projectiles aren’t moving that fast so if you shoot and the shapecast is 1024 studs long it’ll hit players in the distance that may not be there when the actual projectile gets there.

So I’m not sure if this was clear in my code, but the projectiles don’t hit the shapecast limit unless they’re travelling at the shapecast stud limit per frame. The projectiles I showed usually only hit around 1-2 studs per frame. And the length of the cast is decided by calculating where the projectile will be in the next frame. So no I’m not using the size limit, the length of the cast is relative to how fast the projectile is travelling.

1 Like

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