Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

How can I check whether a player is looking at a part?

I want to make a Weeping Angel-Like monster, but I do not know where to start. I've seen this post: https://scriptinghelpers.org/questions/25213/how-can-i-create-mechanics-similar-to-dont-blink which has this code:

	local camera = game.Workspace.CurrentCamera
	local worldPosition = Vector3.new()
	local screenPosition,  onScreen = camera:WorldToScreenPoint(worldPosition)
	 
	if onScreen then
	    print'Player can see the point!'
	else
	    print'Player cannot see the point :('
	end

which started to work, but after I tested it, it broke completely. the part was named CurrentCamera, as it said. Anything I can do?

1 comment

0

you could try raycasting for the part

2 answers

✓ Accepted answer

Alright, let's start with this:

local offset, onScreen = Camera:WorldToScreenPoint(point)

This is a function that returns:

  1. offset, which is a Vector3 describing how far point is from the camera screen, and
  2. onScreen, which tells you if that point is inside the screen.

So we're going to use this to detect whether that creature is on screen, or not.

First and probably the most intuitive implementation would be to check if the monster's position(well, the position of the PrimaryPart of the monster model, or of the HumanoidRootPart) is inside the screen at every frame, like so:

RunService.Heartbeat:Connect(function() 
	local point = Monster.HumanoidRootPart.Position
 	local offset, onScreen = Camera:WorldToScreenPoint(point)
	if onScreen then
		-- point is on screen
	else
		-- point is not on screen
	end
end)

Cool, but that only checks if the point is on screen. You want to know if a player is "looking at" the point, which is to say that the point has to be somewhere in the center of the screen. For this, we could use the offset property like so:

  1. Find out where the point is in the screen using offset
  2. Check how far that point is from the center of the screen
  3. If it's less than a certain radius, then you could say that the part is in the center

To implement that in code, you'd have to know how to get the distance between two points using the distance formula. Here's what that looks like: (note that this script isn't optimized at all)

RunService.Heartbeat:Connect(function() 
	local ViewportSize = Camera.ViewportSize
	local point = Monster.HumanoidRootPart.Position
 	local offset, onScreen = Camera:WorldToScreenPoint(point)
	if onScreen then
		-- point is on screen
		
		-- get the offset, then screen size
		local offsetX, offsetY= offset.X, offset.Y
		local viewportX, viewportY = ViewportSize.X, ViewportSize.Y

		-- this is how far from the center we'd call "close enough"
		local radius = 100

		-- get the distance from the center
		local dist = math.sqrt(
			((viewportX/2) - offsetX)^2 + ((viewportY/2) - offsetY)^2 
		)

		-- check
		if dist <= radius then -- distance formula
			-- point is at the center of the screen
		else
			-- point is not at the center
		end
	else
		-- point is not on screen
	end
end)

Cool, now we know if a part is roughly at the center of the screen, and maybe this is enough to roughly say that a player is looking at the part.

But... monsters are made up of lots of parts. So maybe we could loop through all of those parts, and see if the player is looking at any of those parts.

But... this only checks the center of each part, and not the extents of the part. So maybe we could get every corner of the boundingbox of the part, then loop through all of those points instead.

As an optimization, we could just do the centers of top/bottom/left/right faces of the part... or we could just check the extents of the model itself, instead of having to check each individual corner of each individual part in the monster model...

And I almost forgot, we forgot to check if there are any parts that are blocking the view... so you'd have to raycast from those points to the camera to ensure that it's unobstructed...

Yeah, no.

You know what, this might have been getting too complicated, yeah? A more simple system would be to just lock the player in FPS, lock the mouse at the center of the screen, listen to mouse movements, then check if mouse.Hit is a monster's body part.

Hope that helps!

1 comment

1

Thank you for trying though! I think I will use your mouse.Hit idea! Makes it a lot more tense when you have to look at the monster directly then from the corner of your eyes!

local Plr = game:GetService("Players").LocalPlayer
local Mouse = Plr:GetMouse()

Mouse.Button1Down:connect(function()
if not Mouse.Target then return end
print(Mouse.Target.Name)
end)

That would print the name of the part that the localplayer is looking at (if he Button1down's, you can put this in a loop and probably achieve what you need)

1 comment

1

only works in first person and also doesn't take into account the player's 70 degree FOV

This discussion is preserved as it was. New answers, comments, and votes are closed.