CastVisuals | Visualising Raycasts, Spherecasts, and Blockcasts

CastVisuals is a very simple module that allows you to easily visualise your cast operations in any given WorldRoot (ie: Workspace or WorldModel).

API is simple; every function takes the same parameters as their WorldRoot counterparts. All functions are type-safe to help avoid issues.

local CastVisual = CastVisuals.new(Color3.new(1,0,0), workspace) -- both args are optional, Color is Color3.new(0, 0, 1) by default and WorldRoot is Workspace by default.

CastVisual:Raycast(Origin: Vector3, Direction: Vector3, CastParams)
task.wait(1)
CastVisual:Spherecast(Origin: Vector3, Radius: number, Direction: Vector3, CastParams)
task.wait(1)
CastVisual:Blockcast(Origin: CFrame, Size: Vector3, Direction: Vector3, CastParams)

task.wait(5)

CastVisual:Hide() -- hides the visualisation (note: also hides if cast hits nothing)

Visuals can be customised too, there is a list of config options at the top of the script.

Get it here: https://create.roblox.com/marketplace/asset/13437219890/Cast-Visualiser-inc-Spherecasts-Blockcasts

Demo place: Cast Visualiser Demo Place.rbxl (45.5 KB)

76 Likes

Thank you so much for this, this is awesome and so helpful!

1 Like

This is really great, but it would be better if you could see the visual even if the cast didn’t hit. Often I’m not sure I’m actually casting correctly!

4 Likes

This is a neat module, though to save a bit of performance in our games maybe simply make it return the RaycastResult, this way we don’t need to make another raycast for the actual functionality.

Another performance thingy (I like performance in modules lol) is to only create new Instances when they are being used, but don’t destroy them when they aren’t being used since that is also not great. Because right now you’re creating all instances when you construct the class. One might not always use all three of the raycasting methods in the same Visuals object and that creates useless instances.

Btw, great job!

5 Likes

Hello. First off, thank you for your useful module.
However after some use as a debug tool, I have found an oversight for the Blockcast visualizer.

The Box Adornment that you use inherits from the Parent’s CFrame rotation, which does not match with the actual result of the blockcast. In your visualizer, the Box visual is oriented along the Parent’s direction, which should not be the case, and should instead be oriented based on the provided CFrame.

Here’s the fixed function:

function CastVisualiser:Blockcast(CF: CFrame, Size: Vector3, Direction: Vector3, RaycastParameters: RaycastParams?)
	local self: CastVisualiserPrivate = self
	
	local Cast = self.WorldRoot:Blockcast(CF, Size, Direction, RaycastParameters)
	if not Cast then
		self:Hide()
		return
	end
	
	local FinalPos = CF.Position + (Direction.Unit * Cast.Distance)
	
	self.CastOriginPart.CFrame = CFrame.lookAt(CF.Position, FinalPos)
	
	self.LineVisual.Length = Cast.Distance - CONE_HEIGHT
	self.ConeVisual.CFrame = CFrame.new(0, 0, -Cast.Distance)
	
	self.BoxVisual.CFrame = CFrame.new(0,0, -Cast.Distance) * self.BoxVisual.Parent.CFrame.Rotation:Inverse() * CF.Rotation  
	self.BoxVisual.Size = Size
	
	self.BoxVisual.Visible = true
	self.SphereVisual.Visible = false
	
	self.CastOriginPart.Parent = self.WorldRoot:FindFirstChild("Terrain") or self.WorldRoot
end

This allows the box visualizer’s orientation to be fully independent from the box’s parent’s cframe orientation, the same way an actual BlockCast behaves (CFrame not oriented based on the cast direction).
I’ve tested it, and it now matches the actual result of a blockcast.

1 Like

I fixed this a while ago but forgot to update the module, I’ll published the fixed version now.

2 Likes