GetPartBoundsInBox doesn't work but GetPartsInPart does

Problem: GetPartBoundsInBox doesn’t work when the projectiles are arcing and work fine when the projectiles are straight. GetPartsInPart works all the time.

https://gyazo.com/735f643cf0cf339017146da82ac237cd

GetPartBoundsInBox used

https://gyazo.com/e6a8d53e6c330dce0b7dd9614f76e9a7

GetPartsInPart used

Part.Shape = Enum.PartType.Block
Part.CFrame = origin
Part.Size = size
Part.Anchored = true
Part.CanCollide = false
Part.CanTouch = false
Part.CanQuery = false
Part.Color =  Color3.fromRGB(75, 200, 200) or Color3.fromRGB(255,0,0)
Part.Material = Enum.Material.Neon
Part.Transparency = 0.65
Part.Parent = workspace.Garbage
task.delay(1, function()
	Part:Destroy()
end)
local hitbox = workspace:GetPartBoundsInBox(origin, size, params)
local hitbox = workspace:GetPartsInPart(Part, params)

I’m assuming that the region for GetPartBoundsInBox isn’t rotated but then why would it ask for a cframe?

GetPartsInPart works because it uses the actual part’s size and rotation, so it lines up with your projectile no matter how it arcs.
GetPartBoundsInBox is different. It makes a world-aligned box. Even if you pass a CFrame, the box itself doesn’t rotate with your projectile, which is why it looks fine when straight but breaks on arcs.

If you want the hitbox to rotate with your projectile, stick with GetPartsInPart. Save GetPartBoundsInBox for when you specifically need a world-aligned detection area.

Also tiny side note:

Part.Color = Color3.fromRGB(75, 200, 200) or Color3.fromRGB(255,0,0)

The or will always pick the first one. If you want variety, throw your colors in a table and pick with math.random().

1 Like

BRUH, thank you though this is the perfect answer, and you are also right about the color picking.

Glad I was able to help. Happy coding! :slightly_smiling_face:

1 Like

While it uses the bounding boxes, it’s not world-aligned. That’s why it takes a CFrame, as it does actually respect the rotation.

1 Like

I’ve rewatched the videos several times, your projectiles seem to be working in both videos?

I don’t recommend using spatial queries for projectiles, especially if they move quickly, as they can often “clip” through objects if it does not visibly intersect on any frame. I suggest using Shapecasts/Blockcasts instead.

The bullet on the client does collide with the dummy but the server bullet stops before colliding as you can see by the blue parts stopping before the dummy. So GetPartBoundsInBox does respect rotation but the reason for the early collision is because the sphere has a box bounding hitbox? And what do you mean by clipping through objects, do you mean having gaps between the hitboxes if they go fast enough?

Can you show a snippet of the server-side projectile code?
I suspect this might be a different issue.

Yes, generally ray/shapecasting is better for projectiles, as you can compensate for any sort of lag.

You can format code by wrapping it like this:
```lua
print(“Hello World!”)
```

function ProjectileModule.ProjectileStraight(player, model : Model, origin : CFrame, velocity : Vector3, sizecoefficent : Vector3, duration : number, acceleration : Vector3?, params : RaycastParams, destructionbool : boolean, damage : number, explosion : {}, hitinfo : {}, instance, hitposition, debrisbool, rotatebool)

if Connection == nil then
	ProjectileModule.Start()
end
local Character = player.Character
if not Character then return end
local Projectile = {}
local Model : Model = ReplicatedStorage.VFX.Models[model]
if Model:IsA("Model") and Model.PrimaryPart then
	Projectile.Size = Model.PrimaryPart.Size
elseif Model:IsA("BasePart") then
	Projectile.Size = Model.Size
else
	Projectile.Size = Vector3.one
end
if typeof(sizecoefficent) == "Vector3" then
	Projectile.Size = Vector3.new(Projectile.Size.X * sizecoefficent.X, Projectile.Size.Y * sizecoefficent.Y, Projectile.Size.Z * sizecoefficent.Z)
elseif typeof(sizecoefficent) == "number" then
	Projectile.Size *= sizecoefficent
end

Projectile.OriginalSize = Projectile.Size 
Projectile.Origin = origin
Projectile.Velocity = velocity
Projectile.Acceleration = acceleration and acceleration or Vector3.zero
Projectile.Player = player
Projectile.Position = (origin.Position - (origin.LookVector * velocity.Magnitude * 1/60))
Projectile.Duration = duration
Projectile.Params = params
Projectile.Time = 0
Projectile.OnHitDestruct = destructionbool
Projectile.CFrame = CFrame.new()
Projectile.Damage = damage
Projectile.OnHit = OnHit
Projectile.Hit = {}
Projectile.ExplosionParams = explosion
Projectile.HitInfo = hitinfo


--Projectile.Model = Instance.new("Part")
--Projectile.Model.CanCollide = false
--Projectile.Model.CanTouch = false
--Projectile.Model.CanQuery = false
--Projectile.Model.Size = Projectile.Size
--Projectile.Model.Color = Color3.fromRGB(200, 75, 95)
--Projectile.Model.Name = "ServerBullet"
--Projectile.Model.Transparency = .85
--Projectile.Model.Material = Enum.Material.Neon
--Projectile.Model.Anchored = true
--Projectile.Model.CFrame = origin
--Projectile.Model.Parent = workspace.Garbage

GeneralEvent:FireAllClients({Name = "ClientVisuals", FunctionName = "ServerProjectileCall", FunctionArguments = {instance, hitposition, velocity.Magnitude, {origin,velocity,acceleration,sizecoefficent,duration,destructionbool, player.Character, model, Projectile.HitInfo[2], Projectile.HitInfo[3], Projectile.HitInfo[1], Projectile.ExplosionParams[8], Projectile.ExplosionParams[9], debrisbool, rotatebool, Projectile.HitInfo[4], Projectile.HitInfo[5], Projectile.HitInfo[6], Projectile.ExplosionParams[10], Projectile.ExplosionParams[11], Projectile.ExplosionParams[12]}}})
table.insert(Projectiles, Projectile)

end

function Loop(dt)

--print("Bullet Count : ",#Projectiles)
for index, Projectile in Projectiles do
	--if not Projectile then continue end
	if Projectile.Time >= Projectile.Duration then 
		Projectiles[index] = nil
		Projectile = nil
		continue
	end

	local inversespeed = (1/math.max(Projectile.Velocity.Magnitude, 0.01))
	local sizecoefficient = (1 - inversespeed) ^ 2 
	Projectile.Time += dt
	Projectile.Size = Vector3.new(Projectile.Size.X, Projectile.Size.Y, math.clamp((Projectile.Velocity * dt * sizecoefficient * 1.45).Magnitude * Projectile.OriginalSize.Z, Projectile.OriginalSize.Z, 150))
	Projectile.Velocity += Projectile.Acceleration * dt
	Projectile.Position += Projectile.Velocity * dt 
	local velocityDir = Projectile.Velocity.Unit
	Projectile.CFrame = CFrame.new(Projectile.Position, Projectile.Position + velocityDir) * CFrame.new(0, 0, -Projectile.Size.Z / 2)

 
	local hit_table : {Model} = HitboxModule.Projectile(Projectile.CFrame, Projectile.Size, Projectile.Params, Projectile.OnHitDestruct)
	if not hit_table then continue end

	for i, hit : {} in hit_table do
		local damage = Projectile.Damage
		local character = hit.Target
		local function clearProjectile()
			Projectiles[index] = nil
			Projectile = nil
		end
		if table.find(Projectile.Hit, character) then
			continue
		end
		table.insert(Projectile.Hit, character)
		local humanoid : Humanoid = character:FindFirstChild("Humanoid")
		if not humanoid then
			humanoid = character.Parent:FindFirstChild("Humanoid") 
			character = character.Parent
		end
		if not humanoid then
			if i == #hit_table and Projectile.OnHitDestruct then
				Projectile.OnHit(Projectile, "Map", nil, 3, hit.ContactPoint)
				clearProjectile()
			end
			continue
		end
		Damage(Projectile.Player, character, humanoid, damage, false)
		Projectile.OnHit(Projectile, "Player", character, 3, hit.ContactPoint)
		if i == #hit_table and Projectile.OnHitDestruct then
			clearProjectile()
		end
	end
	--print(Projectile.Hit[1], index, Projectile.Hit)
end

ProjectileModule.Projectile makes the bullet data and the Loop function changes the size, position and velocity of each projectile

How do you determine when to stop the projectile?
Your first video shows the server bullet stops well before colliding with anything, do you have it stop when the client registers a hit? if so then that’s likely the problem.

No whenever

local hit_table : {Model} = HitboxModule.Projectile(Projectile.CFrame, Projectile.Size, Projectile.Params, Projectile.OnHitDestruct)

is ran and the bullet is set to destroy on impact it stops the projectile. The client only stops the visual projectile with the trail in the video. The bullet works with GetPartsInPart its just that I think the Bounding Box of the map cause the bullet to stop early when using GetPartBoundsInBox

Oh I see, then you should be using GetPartsInPart (or switch to Block/Shapecasting), but the issue isn’t from the bounding boxes being world-aligned.

1 Like

Yep thanks to both you for helping me out

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