How would you see if a player is facing a model?

Replace this:

if model and model:IsA("Model") then
print(model.Name)
  end

I would recommend doing this on the client, not the server. That way, you can use the camera (which is more “precise” than the head). If you’re in shift lock switch or first person, using the head can cause problems because the head won’t face where you are actually looking, which means you might think that the player is looking at something when they actually aren’t. (this problem doesn’t exist with the camera)

Then, if the player is looking at something, you can fire a RemoteEvent to tell the server to act on this. If you’re concerned about security, then you can validate if the player was actually looking at the object on the server.

If it’s in view with the camera, it’s always in view with the head, but if it’s in view with the head, then it’s not always in view with the camera. That means that if the client tells you it’s in view with the camera, you can validate it on the server using the head.

Here’s what you would do on the client:

-- localscript in StarterPlayerScripts
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TestingRemote = ReplicatedStorage.TestingRemote

local RunService = game:GetService("RunService")
local priority = Enum.RenderPriority.Camera.Value

local Player = game:GetService("Players").LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()

local camera = workspace.CurrentCamera
local position = -- the position you want to check if its looking at

local options = RaycastParams.new()
options.FilterType = Enum.RaycastFilterType.Blacklist
options.IgnoreWater = true

RunService:BindToRenderStep("Check", priority, function()
    local _, onScreen = camera:WorldToViewportPoint(position)

    if onScreen then
        -- direction check passed, now raycast
        options.FilterDescendantsInstances = { Character }

        local cameraPos = camera.CFrame.Position
        local result = workspace:Raycast(
            cameraPos,
            position - cameraPos,
            options
        )

        if result and result.Instance then
            -- it's visible on the client, send it to the server
            TestingRemote:FireServer(position)
        end
    end
end)

Player.CharacterAdded:Connect(function(character)
    Character = character
end)

And then here’s what you would use on the server to validate it:

-- server script in ServerScriptService
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TestingRemote = ReplicatedStorage.TestingRemote

TestingRemote.OnServerEvent:Connect(function(player, lookAt)
    --[[
    client told us that they're looking at it with their camera,
    and passed the Vector3 of what they looked at. We have
    to validate it.
    ]]

    local character = player.Character
    local direction = (lookAt - character.Head.Position).Unit
    local lookVector = character.Head.CFrame.LookVector

    if direction:Dot(lookVector) > 0.5 then
        --// they were looking at it, proceed
    end
end)

The server uses dot product to validate it, and the client uses WorldToViewportPoint and workspace:Raycast() to see if it is on screen. Then, the server can take action if it gets validated. Otherwise, there is a chance the client may have been exploiting, but we still aren’t sure so we can’t take any action on it.

For more information about Dot product, see @okeanskiy’s video here: https://www.youtube.com/watch?v=BUwAcW_18Ws

For more information about Camera:WorldToViewportPoint(), see the developer article here:
https://developer.roblox.com/en-us/api-reference/function/Camera/WorldToViewportPoint

2 Likes

I put @Stredexz code in a local script

You can use his if you want, but the thing I posted doesn’t work with his.