Function returning nil instead of true or false not detecting if a character is visible to the camera?

I’m trying to make a function that will return true of false if the given character is visible to the camera or not.

local IsCharacterVisible = function(Target: Model): boolean
	if Target.PrimaryPart == nil then return false; end;
	
	local Camera = workspace.CurrentCamera;
	local TargetCF = Target.PrimaryPart.Position;
	
	local _, onScreen = Camera:WorldToViewportPoint(TargetCF);
	
	if onScreen then
		local origin = Camera.CFrame.p;
		local ray = Ray.new(origin, TargetCF - origin);
		local hit = workspace:FindPartOnRay(ray);
		if hit then
			return false;
		end;
	else
		return false;
	end;
	
	return true;
end;
2 Likes

If the raycast has no hit it returns nil so use else on that and provide true/false there. Think of every possible scenario and make sure it has a return value.

2 Likes

Its still returning nil after i check if it is nil and do a return false

1 Like

In the current code, hit is never nil. It is either an instance obscuring the view or a part of the target character, like RightUpperArm.

Consider checking whether the hit part is a descendant of the target. In the new version I replaced FindPartOnRay with a newer workspace:Raycast() and also added player’s own character to the ray filter to ignore it.

For easier testing the local script is in StarterCharacterScripts, but with some adjustments you can of course use it anywhere.

Code
local Camera = workspace.CurrentCamera;
local Character = script.Parent;

local RaycastParameters = RaycastParams.new();
RaycastParameters.FilterDescendantsInstances = {Character};
RaycastParameters.FilterType = Enum.RaycastFilterType.Exclude;

local function IsCharacterVisible(Target: Model): boolean
	if Target.PrimaryPart == nil then return false; end;
	
	local TargetPos = Target.PrimaryPart.Position;
	
	local _, OnScreen = Camera:WorldToViewportPoint(TargetPos);
	
	if OnScreen then
		local RaycastResult = workspace:Raycast(
			Camera.CFrame.Position, TargetPos - Camera.CFrame.Position, RaycastParameters
		);
		return if RaycastResult then RaycastResult.Instance:IsDescendantOf(Target) else false;
	else
		return false;
	end;
end;

An interesting alternative is to use camera:GetPartsObscuringTarget(). When there are multiple points to cast towards, I know it is more efficient than raycasting individually, but I don’t know about performance gains for single casts. Anyways, it works the same but the code is a bit shorter.

Code
local Camera = workspace.CurrentCamera;
local Character = script.Parent;

local IgnoreList = {Character};

local function IsCharacterVisible(Target: Model): boolean
	if Target.PrimaryPart == nil then return false; end;
	
	local TargetPos = Target.PrimaryPart.Position;
	
	local _, OnScreen = Camera:WorldToViewportPoint(TargetPos);
	
	if OnScreen then
		local Obstructors = Camera:GetPartsObscuringTarget(table.pack(TargetPos), IgnoreList);
		return next(Obstructors) == nil;
	else
		return false;
	end;
end;
1 Like

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