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;
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.
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;